博客 / 詳情

返回

AuthorizationServerConfigurerAdapter 認證服務器擴展認證方式 例 sms, openId 等

本文介紹一種優雅的擴展AuthorizationServer認證方式的實現方法,可協助實現短信認證,微信openId認證等。


核心相關代碼:

@Configuration
@EnableAuthorizationServer
public class MyAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter
 @Override
    public void configure(
            AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //配置AuthorizationServerEndpointsConfigurer眾多相關類,包括配置身份認證器,配置認證方式,TokenStore,TokenGranter,OAuth2RequestFactory

        // @formatter:off
        endpoints
            .tokenStore(tokenStore)
            .authenticationManager(authenticationManager)
            .userDetailsService(userDetailsService())
            .accessTokenConverter(jwtAccessTokenConverter)
            //設置自定義tokenGranter
            .tokenGranter(customTokenGranter(endpoints))
                .authorizationCodeServices(authorizationCodeServices())
            .setClientDetailsService(jdbcClientDetailsService());
        // @formatter:on

        //擴展token返回結果
        if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
            TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> enhancerList = new ArrayList();
            enhancerList.add(jwtTokenEnhancer);
            enhancerList.add(jwtAccessTokenConverter);
            tokenEnhancerChain.setTokenEnhancers(enhancerList);
            //jwt
            endpoints.tokenEnhancer(tokenEnhancerChain).accessTokenConverter(jwtAccessTokenConverter);
        }
    }

請關注customTokenGranter這個方法,以下為自定義重寫

/**
     * 自定義TokenGranter
     */
    private TokenGranter customTokenGranter(
            AuthorizationServerEndpointsConfigurer endpoints) {
        TokenGranter tokenGranter = new TokenGranter() {
            private CompositeTokenGranter delegate;

            @Override
            public OAuth2AccessToken grant(
                    String grantType,
                    TokenRequest tokenRequest) {
                if (delegate == null) {
                    delegate = new CompositeTokenGranter(getDefaultTokenGranters(endpoints));
                }
                return delegate.grant(grantType, tokenRequest);
            }
        };
        return tokenGranter;
    }
/**
     * 這是從spring 的代碼中 copy出來的, 默認的幾個TokenGranter, 還原封不動加進去.
     * 主要目的是覆蓋原來的List<TokenGranter>,方便我們添加自定義的授權方式
     */
    private List<TokenGranter> getDefaultTokenGranters(
            AuthorizationServerEndpointsConfigurer endpoints) {
        AuthorizationServerTokenServices tokenServices = endpoints.getDefaultAuthorizationServerTokenServices();
        AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices();
        OAuth2RequestFactory requestFactory = endpoints.getOAuth2RequestFactory();
        List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
        tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices,
                endpoints.getClientDetailsService(), requestFactory));
        tokenGranters.add(new RefreshTokenGranter(tokenServices, endpoints.getClientDetailsService(), requestFactory));
        ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, endpoints.getClientDetailsService(),
                requestFactory);
        tokenGranters.add(implicit);
        tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, endpoints.getClientDetailsService(),
                requestFactory));
        if (authenticationManager != null) {
            tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices,
                    endpoints.getClientDetailsService(), requestFactory));

            //追加自定義
            tokenGranters.add(new OpenIdTokenGranter(authenticationManager, tokenServices,
                    endpoints.getClientDetailsService(), requestFactory));
        }

        return tokenGranters;
    }

OpenIdTokenGranter為新追加的方式

public class OpenIdTokenGranter extends AbstractTokenGranter {

    private static final String GRANT_TYPE = "openid";

    private final AuthenticationManager authenticationManager;

    public OpenIdTokenGranter(AuthenticationManager authenticationManager,
                              AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
                              OAuth2RequestFactory requestFactory) {
        this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
    }

    protected OpenIdTokenGranter(AuthenticationManager authenticationManager,
                                 AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
                                 OAuth2RequestFactory requestFactory, String grantType) {
        super(tokenServices, clientDetailsService, requestFactory, grantType);
        this.authenticationManager = authenticationManager;
    }

    @Override
    protected OAuth2Authentication getOAuth2Authentication(
        ClientDetails client,
        TokenRequest tokenRequest) {

        Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
        String openId = parameters.get("openId");

        //UserDetails userData = smsUserDetailService.loadUserByMobile(mobile);

        Authentication userAuth = new OpenIdAuthenticationToken(openId);
        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
        try {
            //具體會使用 OpenIdAuthenticationProvider 的 authenticate 去實現
            userAuth = authenticationManager.authenticate(userAuth);
        }
        catch (AccountStatusException ase) {
            //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
            throw new InvalidGrantException(ase.getMessage());
        }
        catch (BadCredentialsException e) {
            // If the username/password are wrong the spec says we should send 400/invalid grant
            throw new InvalidGrantException(e.getMessage());
        }
        if (userAuth == null || !userAuth.isAuthenticated()) {
            throw new InvalidGrantException("Could not authenticate openId: " + openId);
        }

        OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
        return new OAuth2Authentication(storedOAuth2Request, userAuth);

    }

}
public class OpenIdAuthenticationToken extends AbstractAuthenticationToken {

    private static final long serialVersionUID = 520L;

    private final Object principal;

    private String openId;

    public OpenIdAuthenticationToken(String openId) {
        super(null);
        this.principal = openId;
        this.openId = openId;
        setAuthenticated(false);
    }

    public OpenIdAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true); // must use super, as we override
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException(
                    "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        }

        super.setAuthenticated(false);
    }

    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
    }

    @Override
    public Object getCredentials() {
        return null;
    }

    /**
     * get openId
     * 
     * @return openId
     */
    public String getOpenId() {
        return openId;
    }

}
public class OpenIdAuthenticationProvider implements AuthenticationProvider {

    private UserInfoService userInfoService;

    @Override
    public Authentication authenticate(
        Authentication authentication) throws AuthenticationException {
        //執行校驗
        OpenIdAuthenticationToken openIdAuthenticationToken = (OpenIdAuthenticationToken) authentication;
        userInfoService = SpringBootUtil.getBean(UserInfoService.class);

        String openId = (String) openIdAuthenticationToken.getPrincipal();

        UserDetails userDetails = userInfoService.loadUserByOpenId(openId);

        if (userDetails == null) {
            throw new BadCredentialsException("Invalid openId!");
        }

        return new OpenIdAuthenticationToken(userDetails, userDetails.getAuthorities());
    }

    /**
     * 為了使ProviderManger知道OpenIdAuthenticationToken該使用那個provider處理
     * 
     * @see AuthenticationProvider#supports(Class)
     */
    @Override
    public boolean supports(
        Class<?> authentication) {
        return OpenIdAuthenticationToken.class.isAssignableFrom(authentication);
    }

}
/**
 * 將OpenIdAuthenticationProvider注入,在SecurityConfig中使用http.apply去使用
 **/
@Component
public class AuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

    @Override
    public void configure(
        HttpSecurity http) {

        OpenIdAuthenticationProvider openIdProvider = new OpenIdAuthenticationProvider();
        http.authenticationProvider(openIdProvider);
    }
}

SecurityConfig中,

 @Autowired
    private AuthenticationSecurityConfig authenticationSecurityConfig;


 @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 禁用CSRF攻擊防禦
        http.csrf().disable();

        // 設置跨域
        http.cors();

        String[] matchUris = {"/sso/**", "/login/**", "/logout", "/logout/**", "/oauth/authorize"};

        http.requestMatchers()
                .antMatchers(matchUris).and()
                // 認證授權
                .authorizeRequests()
.authenticated().and().exceptionHandling().accessDeniedPage("/login/denied").and().cors().and()
                //注入自定義驗證provider
                .apply(authenticationSecurityConfig);
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.