복붙노트

[SPRING] 여분의 로그인 파라미터를 가진 봄 기억 나

SPRING

여분의 로그인 파라미터를 가진 봄 기억 나

봄 mvc 응용 프로그램에서 로그인 화면에서 추가 '위치'매개 변수를 캡처하고 사용자 이름 이외에 인증을 위해 사용하고 있습니다. 그래서 'loadUserByUsername'내 SQL 쿼리는 다음과 같습니다.

select from user where username = ? and location = ? 

이제 사용자가 remember-me 사용자 인 경우 로그인 프롬프트가 없으므로 'location'매개 변수를 캡처 할 수있는 방법이 없습니다. remember-me 기능을 사용하는 스프링의 경우 사용자 이름 만 쿠키에 저장합니다. 그리고 remember-me 로그인을 위해 쿠키에서 사용자 이름을 가져 와서 'loadUserByUsername'호출에 전달하여 DB에서 사용자를로드합니다. 그래서, 내 경우에는 remember-me 사용자를 위해 'location'이 null 쿼리가 사용자를로드하는 데 실패하기 때문에 실패합니다. 기본 스프링 동작을 무시하고 사용자 이름과 함께 쿠키에 'location'을 저장 한 다음 PersistentTokenBasedRememberMeServices.processAutoLoginCookie ()에서 'loadUserByUsername'에 위치 및 사용자 이름을 전달하는 방법이 있는지 알고 싶습니다. 참조를 위해 아래 코드를 참고하십시오.

CustomAuthenticationFilter.java :-

public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        final Long locationId = Long.parseLong(request.getParameter("locations"));
        request.getSession().setAttribute("LOCATION_ID", locationId);

        return super.attemptAuthentication(request, response); 
    } 
}

SecurityConfig.java:-

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    DataSource dataSource;

    @Autowired
    private AuthenticationManagerBuilder auth;

    @Autowired
    public void configureGlobal(UserDetailsService userDetailsService, AuthenticationManagerBuilder auth) throws Exception {
        auth
        .userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Autowired
    AccessDeniedExceptionHandler accessDeniedExceptionHandler;

    @Bean
    public CustomInvalidSessionStrategy invalidSessionStrategy() {
        return new CustomInvalidSessionStrategy();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
        .antMatchers("/resources/**").permitAll()
        .antMatchers("/error/**").permitAll()
        .antMatchers("/secured/**").hasRole("ADMIN")
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .loginPage("/login")
//      .defaultSuccessUrl("/")
        .permitAll()
        .and().rememberMe().rememberMeServices(persistentTokenBasedRememberMeServices())
        .and()
        .logout()
        .permitAll()
        .and()
        .exceptionHandling()
        .accessDeniedHandler(accessDeniedExceptionHandler);

        http.addFilterBefore(customAuthenticationFilter(),
                UsernamePasswordAuthenticationFilter.class);
        http.addFilterAfter(rememberMeAuthenticationFilter(),
                UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() {
        AuthenticationManager manager = null;
        try {
            manager = super.authenticationManagerBean();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return manager;
    }


    @Bean
    public SimpleUrlAuthenticationSuccessHandler simpleUrlAuthenticationSuccessHandler() {
        SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
        handler.setDefaultTargetUrl("/");
        return handler;
    }

    @Bean
    public SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() {
        SimpleUrlAuthenticationFailureHandler handler = new SimpleUrlAuthenticationFailureHandler();
        handler.setDefaultFailureUrl("/login?error");
        return handler;
    }

    @Bean
    public CustomAuthenticationFilter customAuthenticationFilter () {
        CustomAuthenticationFilter filter= new  CustomAuthenticationFilter();
        filter.setRequiresAuthenticationRequestMatcher(
                new AntPathRequestMatcher("/login","POST"));
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.setUsernameParameter("username");
        filter.setPasswordParameter("password");
        filter.setAuthenticationSuccessHandler(simpleUrlAuthenticationSuccessHandler());
        filter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler());
        filter.setRememberMeServices(persistentTokenBasedRememberMeServices());
        return filter;
    }

    @Bean
    public RememberMeAuthenticationFilter rememberMeAuthenticationFilter() {
        RememberMeAuthenticationFilter filter = new RememberMeAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());
        filter.setRememberMeServices(persistentTokenBasedRememberMeServices());
        return filter;
    }

    @Bean
    public PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices() {
        PersistentTokenBasedRememberMeServices service = new PersistentTokenBasedRememberMeServices("remember_me_key", userDetailsService, persistentTokenRepository());
        service.setCookieName("remember_me");
        service.setTokenValiditySeconds(864000);
        return service;
    }

    @Autowired
    public UserDetailsService userDetailsService;

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepositoryImpl = new JdbcTokenRepositoryImpl();
        tokenRepositoryImpl.setDataSource(dataSource);
        return tokenRepositoryImpl;
    }
}

해결법

  1. ==============================

    1.기본 스프링 동작을 재정의하고 쿠키에 추가 매개 변수를 저장하려면 Spring의 인터페이스 인 UserDetails를 구현해야합니다. 어떻게하는지 예제가 있습니다.

    기본 스프링 동작을 재정의하고 쿠키에 추가 매개 변수를 저장하려면 Spring의 인터페이스 인 UserDetails를 구현해야합니다. 어떻게하는지 예제가 있습니다.

    package example.userdetails;
    
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    
    import java.util.Collection;
    
    public class CustomUserDetails implements UserDetails {
        private long id;
        private String firstName;
        private String lastName;
        private String login;
        private String password;
        private boolean isAccountNonExpired;
        private boolean isAccountNonLocked;
        private boolean isCredentialsNonExpired;
        private boolean isEnabled;
        private Collection<? extends GrantedAuthority> authorities;
    
        public CustomUserDetails(long id, String firstName, String lastName, String login, String password, boolean isEnabled, Collection<? extends GrantedAuthority> authorities) {
            this.id = id;
            this.firstName = firstName;
            this.lastName = lastName;
            this.login = login;
            this.password = password;
            this.authorities = authorities;
            this.isEnabled = isEnabled;
            this.isCredentialsNonExpired = true;
            this.isAccountNonLocked = true;
            this.isAccountNonExpired = true;
        }
    
        public long getId() {
            return id;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public String getLastName() {
            return lastName;
        }
    
        @Override
        public Collection<? extends GrantedAuthority> getAuthorities() {
            return authorities;
        }
    
        @Override
        public String getPassword() {
            return password;
        }
    
        @Override
        public String getUsername() {
            return login;
        }
    
        @Override
        public boolean isAccountNonExpired() {
            return isAccountNonExpired;
        }
    
        @Override
        public boolean isAccountNonLocked() {
            return isAccountNonLocked;
        }
    
        @Override
        public boolean isCredentialsNonExpired() {
            return isCredentialsNonExpired;
        }
    
        @Override
        public boolean isEnabled() {
            return isEnabled;
        }
    
        public boolean hasRole(String role) {
            for (GrantedAuthority grantedAuthority : authorities) {
                if (grantedAuthority.getAuthority().equals(role)) {
                    return true;
                }
            }
            return false;
        }
    }
    

    로그인 한 사용자를 얻으려면 사용할 수 있습니다.

     CustomUserDetails userDetails = (CustomUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    

    그리고 loadUserByUsername을 사용하려면 UserDetailsService 인터페이스를 구현해야합니다. 예를 들어

    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
        @Autowired
        private UserDAO userDAO;
    
        @Override
        @Transactional
        public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
            User user = userDAO.getUserByLogin(userName);
            if (user == null) {
                throw new UsernameNotFoundException("Wrong login");
            }
            List<GrantedAuthority> authorities = buildUserAuthority(user.getRoles());
            return new CustomUserDetails(user.getUserId(), user.getFirstName(), user.getLastName(), user.getLogin(), user.getPassword(), authorities);
        }
    
        private List<GrantedAuthority> buildUserAuthority(Set<Role> roles) {
            Set<GrantedAuthority> authoritySet = roles.stream().map(role -> new SimpleGrantedAuthority(buildRoleForAuthorization(role.getRole()))).collect(Collectors.toSet());
            return new ArrayList<>(authoritySet);
        }
    }
    
  2. ==============================

    2.PersistentTokenBasedRememberMeServices 클래스를 확장하고 그 메소드를 다음과 같이 재정의하십시오.

    PersistentTokenBasedRememberMeServices 클래스를 확장하고 그 메소드를 다음과 같이 재정의하십시오.

      @Override    
        processAutoLoginCookie(String[] cookieTokens,HttpServletRequest request, HttpServletResponse response){
       super.processAutoLoginCookie(cookieTokens,request,response); // do not provide any implementation to loadUserByUsername() in your CustomUserDetail and add one more method loadUserbyUsenameAndLocation(username, location);
       String location = request.getSession().getAttribute("LOCATION_ID");
       return ((customeUserDetailsService)userDetailsService).loadUserbyUsenameAndLocation(username, location)
    }
    
  3. from https://stackoverflow.com/questions/35570818/spring-remember-me-with-extra-login-parameter by cc-by-sa and MIT license