spring cloud oauth2(二) 自定義授權類型 圖片驗證碼


第一部分:關於授權類型 grant_type 的解析

  1. 每種 grant_type 都會有一個對應的 TokenGranter 實現類。
  2. 所有 TokenGranter 實現類都通過 CompositeTokenGranter 中的 tokenGranters 集合存起來。
  3. 然后通過判斷 grantType 參數來定位具體使用那個 TokenGranter 實現類來處理授權。

第二部分:關於授權登錄邏輯

  1. 每種 授權方式 都會有一個對應的 AuthenticationProvider 實現類來實現。
  2. 所有 AuthenticationProvider 實現類都通過 ProviderManager 中的 providers 集合存起來。
  3. TokenGranter 類會 new 一個 AuthenticationToken 實現類,如 UsernamePasswordAuthenticationToken 傳給 ProviderManager 類。
  4. ProviderManager 則通過 AuthenticationToken 來判斷具體使用那個 AuthenticationProvider 實現類來處理授權。
  5. 具體的登錄邏輯由 AuthenticationProvider 實現類來實現,如 DaoAuthenticationProvider

所有的授權類型都會繼承 AbstractTokenGranter

驗證碼模式其實就是用戶名密碼模式,無非在校驗驗證碼的時候先檢查驗證碼是否正確

所有我們直接復制 ResourceOwnerPasswordTokenGranter ,在 getOAuth2Authentication 中增加驗證碼相關邏輯

實現圖片驗證碼模式

  • 新建一個 CaptchaTokenGranter 繼承 AbstractTokenGranter 申明GRANT_TYPE為驗證碼類型*captcha* ,復制上訴密碼模式的TokenGranter
  • 其中有兩個構造函數,第一個是在配置CaptchaTokenGranter時調用,寧外一個是自己內部調用
package com.Lonni.oauth.granter;

import com.Lonni.common.constant.AuthConstant;
import com.Lonni.common.utils.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 圖片驗證碼類型 tokenGranter
 */
public class CaptchaTokenGranter extends AbstractTokenGranter {


    private static   final String  GRANT_TYPE="captcha";
    private final  AuthenticationManager authenticationManager;
    private RedisTemplate redisTemplate;
    public CaptchaTokenGranter(AuthenticationManager authenticationManager,
                               RedisTemplate redisTemplate,
                               AuthorizationServerTokenServices tokenServices,
                               ClientDetailsService clientDetailsService,
                               OAuth2RequestFactory requestFactory) {
        this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        this.redisTemplate=redisTemplate;
    }
    protected CaptchaTokenGranter(AuthenticationManager authenticationManager,
                                  AuthorizationServerTokenServices tokenServices,
                                  ClientDetailsService clientDetailsService,
                                  OAuth2RequestFactory requestFactory,
                                  String grantType) {
        //調用父類 接管GRANT_TYPE類型
        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 username = parameters.get("username");
        String password = parameters.get("password");
        String captcha=parameters.get("captcha");
        if (StringUtils.isEmpty(captcha)){
            throw  new  InvalidGrantException("驗證碼不能為空!");
        }
        //正式環境放行

//        String key= AuthConstant.AUTH_CATCHA_CACHE_KEY+captcha;
//        Object o = redisTemplate.opsForValue().get(key);
//        if (o==null){
//            throw  new  InvalidGrantException("驗證碼不存在!");
//        }
//        if (!o.toString().equals(captcha)){
//            throw  new  InvalidGrantException("驗證碼錯誤!");
//        }
        // Protect from downstream leaks of password
        parameters.remove("password");

        Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
        ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
        try {
            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 user: " + username);
        }

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


    }
}

建立TokenGranter擴展類,將自定義的TokenGranter加入到系統默認的列表中

package com.Lonni.oauth.granter;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.CompositeTokenGranter;
import org.springframework.security.oauth2.provider.TokenGranter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * TokenGranter 擴展 將自定義的grant_type類型添加到oauth2中
 * 使用方法:
 * 在configure(AuthorizationServerEndpointsConfigurer endpoints)中:
 * //獲取自定義tokenGranter
 *      TokenGranter tokenGranter = TokenGranterExt.getTokenGranter(authenticationManager, endpoints, baseRedis, userClient, socialProperties);
 *      endpoints.tokenGranter(tokenGranter);
 *
 */
public class TokenGranterExt {

    public static TokenGranter getTokenGranter(final AuthenticationManager authenticationManager,
                                               final AuthorizationServerEndpointsConfigurer endpointsConfigurer,
                                               RedisTemplate redisTemplate
    ) {

        //  默認tokenGranter集合 security 自帶的
        List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpointsConfigurer.getTokenGranter()));
        //添加驗證碼
        granters.add(new CaptchaTokenGranter(authenticationManager,  redisTemplate, endpointsConfigurer.getTokenServices(), endpointsConfigurer.getClientDetailsService(), endpointsConfigurer.getOAuth2RequestFactory()));
        return new CompositeTokenGranter(granters);
    }

}

在授權服務配置中加入所有的TokenGranter

在 AuthorizationServerConfigurerAdapter 實現類的 configure*(*AuthorizationServerEndpointsConfigurer endpoints*)*方法增加

//獲取grant_type類型組合
        TokenGranter tokenGranter = TokenGranterExt.getTokenGranter(authenticationManager, endpoints,  redisTemplate);


        endpoints
                //設置密碼模式認證器
                .authenticationManager(authenticationManager)
                //設置授權碼模式認證器
                .authorizationCodeServices(this.authorizationCodeServices())
                //.authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource))

                //設置令牌策略
                .tokenServices(tokenServices())
                //設置查詢用戶的userDetilService
                .userDetailsService(userDetailsService)
                //設置grant_type類型集合
                .tokenGranter(tokenGranter)

                //允許post get提交認證
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM