복붙노트

[SPRING] Spring Security 2.06에서 커스텀 AuthenticationProvider 구현하기

SPRING

Spring Security 2.06에서 커스텀 AuthenticationProvider 구현하기

스프링 보안을 사용하여 Struts2 웹 애플리케이션을 보호하고 있습니다. 프로젝트 제약 때문에 Spring Security 2.06을 사용하고 있습니다.

우리 팀은 사용자 이름과 암호 매개 변수를받은 후 사용자를 인증하는 사용자 정의 사용자 관리 API를 작성하고 전자 메일, 이름 등과 같은 역할 및 기타 속성 목록을 포함하는 사용자 정의 사용자 객체를 반환합니다.

Spring Security의 일반적인 유스 케이스에서는 기본 UserDetailsService를 사용하여 UserDetails 객체를 검색합니다. 이 객체는 다른 것들 중에서도 사용자를 인증하기 위해 프레임 워크에서 사용할 암호 필드를 포함합니다.

필자의 경우 맞춤 API가 인증을 수행하도록 한 다음 역할 및 기타 속성 (전자 메일 등)이 포함 된 사용자 정의 UserDetails 객체를 반환하려고합니다.

몇 가지 연구 끝에, 나는 AuthenticationProvider의 커스텀 구현을 통해 이것을 할 수 있다고 생각했다. 또한 UserDetailsService 및 UserDetails의 사용자 지정 구현이 있습니다.

내 문제는 CustomAuthenticationProvider에서 반환 할 내용을 실제로 이해하지 못한다는 것입니다. 내 사용자 정의 UserDetailsService 객체를 여기에서 사용합니까? 그게 필요한가요? 미안 해요. 정말 혼란 스럽네요.

CustomAuthenticationProvider :

public class CustomAuthenticationProvider implements AuthenticationProvider {

private Logger logger = Logger.getLogger(CustomAuthenticationProvider.class);

private UserDetailsService userDetailsService; //what am i supposed to do with this?

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
    String username = String.valueOf(auth.getPrincipal());
    String password = String.valueOf(auth.getCredentials());

    logger.info("username:" + username);
    logger.info("password:" + password);
    /* what should happen here? */

    return null;  //what do i return?
}

@Override
public boolean supports(Class aClass) {
    return true;  //To indicate that this authenticationprovider can handle the auth request. since there's currently only one way of logging in, always return true
}

public UserDetailsService getUserDetailsService() {
    return userDetailsService;
}

public void setUserDetailsService(UserDetailsService userDetailsService) {
    this.userDetailsService = userDetailsService;
}

}

applicationContext-security.xml :

<beans:bean id="customUserDetailsService" scope="prototype" class="com.test.testconsole.security.CustomUserDetailsService"/>

<beans:bean id="customAuthenticationProvider" class="com.test.testconsole.security.CustomAuthenticationProvider">
    <custom-authentication-provider />
    <beans:property name="userDetailsService" ref="customUserDetailsService" />
</beans:bean>

요약하면, 이것이 내가 필요한 것입니다 :

나는 이것이 의미가 있기를 바랍니다. 어떤 도움이나 조언을 부탁드립니다!

감사!

최신 정보:

나는 진전을 보였습니다.

인증 인터페이스를 구현하는 사용자 지정 인증 개체가 있습니다.

public class CustomAuthentication implements Authentication {

    String name;
    GrantedAuthority[] authorities;
    Object credentials;
    Object details;
    Object principal;
    boolean authenticated;

    public CustomAuthentication(String name, GrantedAuthority[] authorities, Object credentials, Object details, Object principal, boolean
                                authenticated){
        this.name=name;
        this.authorities=authorities;
        this.details=details;
        this.principal=principal;
        this.authenticated=authenticated;

    }
    @Override
    public GrantedAuthority[] getAuthorities() {
        return new GrantedAuthority[0];  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Object getCredentials() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Object getDetails() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public Object getPrincipal() {
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public boolean isAuthenticated() {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public String getName() {
        return null;  
    }
}

내 CustomerAuthenticationProvider 클래스를 업데이트했습니다.

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
        String username = String.valueOf(auth.getPrincipal());
        String password = String.valueOf(auth.getCredentials());

        logger.info("username:" + username);
        logger.info("password:" + password);

        //no actual validation done at this time

        GrantedAuthority[] authorities = new GrantedAuthorityImpl[1];
        authorities[0] = new GrantedAuthorityImpl("ROLE_USER");

        CustomAuthentication customAuthentication = new CustomAuthentication("TestMerchant",authorities,"details",username,password,true);

    return customAuthentication;

    //return new UsernamePasswordAuthenticationToken(username,password,authorities); 
}

UsernamePasswordAuthenticationToken 개체를 반환하면 작동하지만 CustomAuthentication을 반환하려고하면 다음 오류가 발생합니다.

java.lang.ClassCastException: com.test.testconsole.security.CustomAuthentication cannot be cast to org.springframework.security.providers.UsernamePasswordAuthenticationToken
    at com.test.testconsole.security.CustomAuthenticationProvider.authenticate(CustomAuthenticationProvider.java:27)
    at org.springframework.security.providers.ProviderManager.doAuthentication(ProviderManager.java:188)
    at org.springframework.security.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:46)
    at org.springframework.security.intercept.AbstractSecurityInterceptor.authenticateIfRequired(AbstractSecurityInterceptor.java:319)
    at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:258)
    at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106)
    at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.SessionFixationProtectionFilter.doFilterHttp(SessionFixationProtectionFilter.java:67)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:116)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.basicauth.BasicProcessingFilter.doFilterHttp(BasicProcessingFilter.java:174)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:278)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
    at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
    at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
    at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:236)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
    at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:536)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:915)
    at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:539)
    at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:405)
    at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
    at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

마치 뭔가 인증 객체가 아니라 특정 구현 인 UsernamePasswordAuthenticationToken을 기대하는 것과 같습니다. 이것은 내가 다른 사용자 정의 구성 요소가 누락되었을 수 있다고 생각하게 만듭니다. 아마 필터일까요?

해결법

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

    1.고유 한 AuthenticationProvider를 구현하는 경우 원하지 않으면 UserDetailsService를 구현할 필요가 없습니다. UserDetailsService는 사용자 정보를로드하기위한 표준 DAO를 제공하며 프레임 워크 내의 다른 클래스는이를 사용하도록 구현됩니다.

    고유 한 AuthenticationProvider를 구현하는 경우 원하지 않으면 UserDetailsService를 구현할 필요가 없습니다. UserDetailsService는 사용자 정보를로드하기위한 표준 DAO를 제공하며 프레임 워크 내의 다른 클래스는이를 사용하도록 구현됩니다.

    일반적으로 사용자 이름과 암호를 사용하여 인증하려면 DaoAuthenticationProvider를 인스턴스화하고이를 UserDetailsService에 주입하십시오. 여전히 최선의 접근 방식 일 수 있습니다. 자신의 공급자를 구현하는 경우 사용자가 올바른 암호를 제공했는지 확인해야합니다. 그러나 어떤 경우에는 이것은 더 간단한 접근법입니다.

    당신의 "여기에 무슨 일이 일어날 것인가?" 코드에 주석을 달아주세요.

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
      UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
      String username = String.valueOf(auth.getPrincipal());
      String password = String.valueOf(auth.getCredentials());
    
      logger.info("username:" + username);
      logger.info("password:" + password); // Don't log passwords in real app
    
      // 1. Use the username to load the data for the user, including authorities and password.
      YourUser user = ....
    
      // 2. Check the passwords match (should use a hashed password here).
      if (!user.getPassword().equals(password)) {
        throw new BadCredentialsException("Bad Credentials");
      }
    
      // 3. Preferably clear the password in the user object before storing in authentication object
      user.clearPassword();
    
      // 4. Return an authenticated token, containing user data and authorities  
    
      return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()) ;
    }
    

    그런 다음 사용자 개체는

    Authentication.getPrincipal()
    

    메소드를 사용하고 사용자 정의 사용자 구현에 캐스팅하여 추가 특성 (이메일 등)에 액세스 할 수 있습니다.

    사용자 데이터를로드하는 방법은 귀하에게 달려 있습니다. 여기에 대한 모든 Spring Security는 AuthenticationProvider 인터페이스입니다.

    또한 해시 된 암호를 저장하고 간단한 동일성 검사가 아닌 동일한 알고리즘을 사용하여 제공된 암호의 유효성을 검사해야합니다.

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

    2.이 루크를 게시 해 주셔서 감사합니다!

    이 루크를 게시 해 주셔서 감사합니다!

    더 많은 뇌 손상으로부터 나를 구 했어요.

    내가 신경 쓰는 사람을 위해 내가 만난 메모의 유일한 것 :

    내 설정 :

    루크 (Luke)가 제안한 커스텀 UserDetails (또는 UserDetailsService) 객체를 구현하지 않고, 특별한 것을 확장하지 않는 자신 만의 사용자 도메인 객체를 사용하지 않으면 서 간단하고 세련된 방식을 활용할 때 " 초 "사용자 정의 태그를 봄 보안에서 (당연히 귀하의 페이지에서) :

    스프링 사용자 정의 간격 태그를 작동 시키려면 기본 맞춤형 UsernamePasswordAuthenticationToken을 인스턴스화 할 때 Principal을 확장하는 무언가의 인스턴스화를 다시 전달해야합니다. 나는 이것을 가능한 한 간단하게 유지하기 위해 다음과 같이했다. 유용하고 적절한 곳에서 내 사용자 도메인 객체 값을 참조한다.

    def principalUser = new org.springframework.security.core.userdetails.User(user.username, user.password, user.enabled, !user.accountExpired, !user.passwordExpired,!user.accountLocked, authorities)
    def token = new UsernamePasswordAuthenticationToken(principalUser, presentedPassword, authorities)
    

    이것은 grails.plugins.springsecurity.SecurityTagLib.determineSource ()에서 테스트 한 조건을 만족해야합니다. 를 사용하는 페이지는 실제로 렌더링 할 것입니다.

    if (principal.metaClass.respondsTo(principal, 'getDomainClass')) {
                return principal.domainClass
    }
    

    그렇지 않으면 사용자 도메인 객체로 UsernamePasswordAuthenticationToken을 인스턴스화하면 (예를 들어 Luke처럼) 보안 태그 lib 메소드 (decideSource ())는 최상위 레벨을 수행하고 org.codehaus.groovy의 (메타) 값을 반환합니다. .grails.commons.DefaultGrailsDomainClass 태그가 username 회원 변수를 찾으면 오류가 발생합니다.

     Error executing tag <sec:ifLoggedIn>: Error executing tag <sec:loggedInUserInfo>: No such property: username for class: org.codehaus.groovy.grails.commons.DefaultGrailsDomainClass
    

    Grails 프로젝트에서 spring-security-core plugin 태그 라이브러리를 다시 구현 / 서브 클래 싱하지 않는다면, taglibs를 사용하고 사용자 정의 도메인 User 클래스를 사용하여 필터에서 공급자에게 전달되는 토큰을 인스턴스화 할 방법이 없습니다.

    그런 다음 코드의 한 줄을 추가로 지불하면 매우 저렴합니다.

  3. from https://stackoverflow.com/questions/8649818/implement-custom-authenticationprovider-in-spring-security-2-06 by cc-by-sa and MIT license