복붙노트

[SPRING] 로그인 페이지에 사용자 이름과 비밀번호를 제외한 다른 필드가있는 경우 Spring 보안을 구현하는 방법은 무엇입니까?

SPRING

로그인 페이지에 사용자 이름과 비밀번호를 제외한 다른 필드가있는 경우 Spring 보안을 구현하는 방법은 무엇입니까?

사용자가 아래 정보를 입력해야하는 로그인 페이지가 있습니다. VIN 번호, 이메일, 우편 번호 및 다른 응용 프로그램에서 가져올 액세스 코드.

따라서 사용자의 유효성을 검사하려면 사용자 정의 UserDetailsService 클래스의 모든 정보가 필요하며 사용자를 인증하는 프로 시저가 호출됩니다.

그러나 나는 아래처럼 UserDetailsService를 구현할 때 그것을 보았다.

@Component
 public class LoginService implements UserDetailsService {
@Autowired
LoginStoredProcedureDao loginStoredProcedureDao;

public Map<String, Object> verifyLogin(LoginDetails details) {
    return loginStoredProcedureDao.verifyLogin(details);

}
@Override
public UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException {
    // TODO Auto-generated method stub
      //verifyLogin();
    return null;
}

}

loginDetails 객체는 아래와 같습니다.

public class LoginDetails {
String vin;
String email;
String zipcode;
String accessCode;
}

위의 상황에서 봄 보안을 사용하는 방법. 여기서 사용자는 자신을 검증하기 위해 모든 정보를 제공해야합니다.

해결법

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

    1.우선, 나는 당신의 문제를 다르게 해결할 것입니다. 나는 다중 단계 인증을 할 것이다. 첫 번째는 스프링 보안의 기본 모델을 사용하는 전통적인 사용자 이름 / 비밀번호 로그인입니다. 두 번째 단계는 사용자가 인증을 위해 추가 세부 정보를 제공하기 위해 채워야하는 다른 양식을 보여주는 것입니다.이 양식은 응용 프로그램에서 시행하려고합니다.

    우선, 나는 당신의 문제를 다르게 해결할 것입니다. 나는 다중 단계 인증을 할 것이다. 첫 번째는 스프링 보안의 기본 모델을 사용하는 전통적인 사용자 이름 / 비밀번호 로그인입니다. 두 번째 단계는 사용자가 인증을 위해 추가 세부 정보를 제공하기 위해 채워야하는 다른 양식을 보여주는 것입니다.이 양식은 응용 프로그램에서 시행하려고합니다.

    그럼에도 불구하고 스프링 보안 모델을 계속해서 커스터마이징하여 한 번에 로그인하는 방법에 대해 자세히 알고 싶다면. @Petr의 이전 대답의 단계 참조를 따릅니다. UserDetailsService 클래스의 세션 속성에 액세스하려면 Spring에서 제공하는 http://static.springsource.org/spring/docs/2.0.8/api/org/springframework/web/context/request/RequestContextHolder.html 클래스를 사용하십시오. .

    RequestAttributes 객체를 반환하는 currentRequestAttributes ()에 대한 액세스 권한을 얻을 수 있습니다. RequestAttributes 객체를 쿼리하여 원하는 범위에서 원하는 속성을 가져올 수 있습니다.

    참고 :이 방법은 정적 방법이므로 단위 테스트에 익숙하지 않습니다.

    기본 HttpServletRequest에 액세스하려는 경우 RequestAttributes를 ServletRequestAttributes로 다운 캐스트 할 수도 있습니다.

    희망이 도움이됩니다.

  2. ==============================

    2.인증 토큰의 유효성을 검사하는 것은 UserDetailsService의 책임이 아닙니다. 이것은 AuthenticationProvider가하는 것입니다.

    인증 토큰의 유효성을 검사하는 것은 UserDetailsService의 책임이 아닙니다. 이것은 AuthenticationProvider가하는 것입니다.

    먼저 UserDetailsService 구현시 로그인으로 데이터베이스에서 사용자의 모든 데이터를로드해야합니다.

    @Component
    public class UserDetailsServiceImpl implements UserDetailsService {
    
        private final UserRepository userRepository;
    
        @Autowired
        public UserDetailsServiceImpl(UserRepository userRepository) {
            this.userRepository = userRepository;
        }
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = null;
            try {
                user = userRepository.findByUsername(username);
            } catch (NotFoundException e) {
                throw new UsernameNotFoundException(String.format("No user found for username %s!", username);
            }
            retrun new UserDetailsImpl(user);
        }
    }
    

    AuthenticationDetailsSource를 구현하는 데 필요한 로그인 양식의 추가 매개 변수를 가로 채기보다. WebAuthenticationDetails를 확장하는 것이 좋습니다. 그러나 AuthenticationDetailsSource에서 반환 한 객체 만 가질 수 있습니다.

    @Component
    public class WebAuthenticationDetailsSourceImpl implements AuthenticationDetailsSource<HttpServletRequest, MyWebAuthenticationDetails> {
    
        @Override
        public MyWebAuthenticationDetails buildDetails(HttpServletRequest context) {
            // the constructor of MyWebAuthenticationDetails can retrieve
            // all extra parameters given on a login form from the request
            // MyWebAuthenticationDetails is your LoginDetails class
            return new MyWebAuthenticationDetails(context);
        }
    }
    

    그리고 검증을 수행하려면 인터페이스 자체를 구현하거나 AbstractUserDetailsAuthenticationProvider 또는 DaoAuthenticationProvider를 확장하여 자신의 AuthenticationProvider를 구현하십시오.

    @Component
    public class UserDetailsAuthenticationProviderImpl extends AbstractUserDetailsAuthenticationProvider {
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            MyWebAuthenticationDetails detais = (MyWebAuthenticationDetails) authentication.getDetails();
            // verify the authentication details here !!!
            // and return proper authentication token (see DaoAuthenticationProvider for example)
        }
    }
    

    AuthenticationManager 및 UsernamePasswordAuthenticationFilter에 구현을 전달하기 만하면됩니다.

    <util:list id="authenticationProviders">
        <ref bean="userDetailsAuthenticationProviderImpl" />
    </util:list>
    
    <!-- 
        This bean MUST have this exact ID to be the default authenticationManager!
        This is required prior Spring 3.1, as authentication-manager-ref is not
        present in sec:http element before!
     -->
    <bean id="org.springframework.security.authenticationManager"
        name="authenticationManager"
        class="org.springframework.security.authentication.ProviderManager"
        c:providers-ref="authenticationProviders" />
    
    <bean id="usernamePasswordAuthenticationFilter"
        class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter"
        p:authenticationManager-ref="authenticationManager"
        p:authenticationDetailsSource-ref="webAuthenticationDetailsSourceImpl" />
    
    <sec:http authentication-manager-ref="authenticationManager">
        <sec:custom-filter position="FORM_LOGIN_FILTER" ref="usernamePasswordAuthenticationFilter" />
    </sec:http>
    

    희망이 도움이!

    추신 필드 주입보다 생성자 주입을 고려하십시오! 그것은 더 테스트 가능하고 클래스의 계약을 더 잘 말해줍니다. 이 토론을 참조하십시오.

  3. ==============================

    3.여기에 답이 있습니다. 로그인 폼에 매개 변수를 추가하려면 필터를 구현하고 기본 필터를 재정의해야합니다.

    여기에 답이 있습니다. 로그인 폼에 매개 변수를 추가하려면 필터를 구현하고 기본 필터를 재정의해야합니다.

  4. ==============================

    4.감사. 사용자 이름, 암호 및 계정 ID의 세 가지 매개 변수를 기반으로 사용자를 인증하기위한 사용자 지정 필터 클래스를 만들었습니다. 나는 SecurityConfig 클래스의 bean으로 autowired했다.

    감사. 사용자 이름, 암호 및 계정 ID의 세 가지 매개 변수를 기반으로 사용자를 인증하기위한 사용자 지정 필터 클래스를 만들었습니다. 나는 SecurityConfig 클래스의 bean으로 autowired했다.

    @Bean
    public AccountCredentialsAuthenticationFilter accountCredentialsAuthenticationFilter()
            throws Exception {
        AccountCredentialsAuthenticationFilter accountCredentialsAuthenticationFilter = new AccountCredentialsAuthenticationFilter();
        accountCredentialsAuthenticationFilter
                .setAuthenticationManager(authenticationManagerBean());
        return accountCredentialsAuthenticationFilter;
    }
    

    따라서 전통적인 사용자 이름과 암호 필드 대신에 인증에 필요한 적절한 서비스 메소드를 호출하고 로그인 한 사용자에 대한 권한을 설정하여 세 가지 필드 (사용자 이름, 비밀번호 및 계정 ID)를 사용하여 인증을 수행 할 수있었습니다.

    public class AccountCredentialsAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    
    @Autowired
    private UserService userService;
    
    @Qualifier("authenticationManager")
    protected AuthenticationManager authenticationManager;
    
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {
    
        String account = request.getParameter("account");
        final String userName = request.getParameter("userName");
        final String password = request.getParameter("password");
    
        boolean isFound = userService.checkLogin(userName, password, account);
    
        if (isFound == true) {
            boolean selectedAccount = false;
            UserDetails userDetails = userService.loadUserByUsername(userName);
    
            User user = (User) userDetails;
            Set<Account> accounts = user.getAccounts();
            String acctSelect = null;
            // user has multiple accounts
            for (Account acct : accounts) {
                acctSelect = acct.getAccountId().toString();
                if (acctSelect.equals(account)) {
                    // confirm which account user has logged in with
                    selectedAccount = true;
    
                    account = acctSelect;
                    request.getSession().setAttribute("account", account);
    
                    break;
                }
            }
    
            if (selectedAccount) {
    
                Set<? extends GrantedAuthority> authorities = (HashSet<? extends GrantedAuthority>) userDetails
                        .getAuthorities();
    
                UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userName, password,
                        authorities);
    
                token.setDetails(new WebAuthenticationDetails(request));
    
                super.setDetails(request, token);
    
                Authentication auth = this.getAuthenticationManager().authenticate(token);
                SecurityContext securityContext = SecurityContextHolder.getContext();
                securityContext.setAuthentication(auth);
                // Create a new session and add the security context.
                HttpSession session = request.getSession(true);
                session.setAttribute("SPRING_SECURITY_CONTEXT", securityContext);
    
                return auth;
    
            } else {
    
                SecurityContextHolder.getContext().setAuthentication(null);
                request.getSession().setAttribute("SPRING_SECURITY_CONTEXT", null);
    
                throw new UsernameNotFoundException("Please input correct credentials");
            }
    
        } else {
    
            SecurityContextHolder.getContext().setAuthentication(null);
            request.getSession().setAttribute("SPRING_SECURITY_CONTEXT", null);
    
            throw new UsernameNotFoundException("Please input correct credentials");
        }
    
    }
    

    인증 및 권한 부여 후 적절한 리디렉션을 위해 UsernamePasswordAuthenticationFilter 클래스의 다음 메소드를 무효화했습니다.

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
            Authentication authResult) throws IOException, ServletException {
        RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
        redirectStrategy.sendRedirect(request, response, "/home");
    
    }
    
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException failed) throws IOException, ServletException {
        RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    
        redirectStrategy.sendRedirect(request, response, "/login?error=true");
    
    }
    

    또한 사용자 지정 필터를 실행하도록 SecurityConfig 클래스의 configure 메서드를 수정했습니다.

       @Override
    protected void configure(HttpSecurity http) throws Exception {  
    
        http.addFilterBefore(accountCredentialsAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)  
        .authorizeRequests()....rest of the code....}
    

    Spring Security의 커스텀 인증을 위해서, 메소드

     @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response){---- call service methods here ----}
    

    이 필터 클래스 (AccountCredentialsAuthenticationFilter)는 컨트롤러 클래스에서 다음 메서드를 중복되게 만듭니다.

     @RequestMapping(value = { "/login" }, method = RequestMethod.POST)
    
       public String loginPage(@Valid @ModelAttribute("user") User user, BindingResult result, ModelMap model, HttpServletRequest request){---- call ervice methods here ----}
    
  5. from https://stackoverflow.com/questions/16104228/how-implement-spring-security-when-login-page-having-more-field-apart-from-user by cc-by-sa and MIT license