您好,登錄后才能下訂單哦!
這期內(nèi)容當(dāng)中小編將會給大家?guī)碛嘘P(guān)怎么看待Spring security oauth2的認(rèn)證流程,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。
隱式授權(quán)模式(Implicit Grant)
需要提供的參數(shù) 地址: oauth/token 請求頭參數(shù): Authorization=Basic base64.encode(client_id:client_secret) ==如果secret為空,只對client_id進(jìn)行編碼== POST參數(shù) grant_type:password username: 用戶名 password:密碼
client_credentials 認(rèn)證
需要傳遞的參數(shù) 地址: oauth/token Header參數(shù) client_id: client_secret: Header頭添加 Authorization=Basic base64.encode(client_id:client_secret) ==如果secret為空,只對client_id進(jìn)行編碼== Post參數(shù) grant_type: password scope: 作用域
password 模式
需要傳遞的參數(shù) 地址: oauth/token Header參數(shù) client_id: client_secret: Header頭添加 Authorization=Basic base64.encode(client_id:client_secret) ==如果secret為空,只對client_id進(jìn)行編碼== Post參數(shù) grant_type: password username: 用戶名 password: 密碼 scope: 作用域
授權(quán)碼模式
1.首先請求oauth/authorize 獲取code 需要提交的參數(shù) Header參數(shù) client_id: client_secret: Header頭添加 Authorization=Basic base64.encode(client_id:client_secret) 如果secret為空,只對client_id進(jìn)行編碼 post參數(shù) grant_type: password username: 用戶名 password: 密碼 scope: 作用域 2. 通過code 訪問 oauth/token接口,換取回應(yīng)的accessToken
首先開啟@EnableAuthorizationServer 注解
AuthorizationServerConfigurerAdapterm默認(rèn)方法配置
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer { @Override public void configure(AuthorizationServerSecurityConfigurer security ) throws Exception { // 配置AuthorizationServer安全認(rèn)證的相關(guān)信息,創(chuàng)建ClientCredentialsTokenEndpointFilter核心過濾器 } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { //配置OAuth3的客戶端相關(guān)信息 } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { //配置AuthorizationServerEndpointsConfigurer眾多相關(guān)類,包括配置身份認(rèn)證器,配置認(rèn)證方式,TokenStore,TokenGranter,OAuth3RequestFactory }
客戶端身份認(rèn)證核心過濾器ClientCredentialsTokenEndpointFilter
核心代碼如下
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { ... String clientId = request.getParameter("client_id"); String clientSecret = request.getParameter("client_secret"); ... clientId = clientId.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId, clientSecret); return this.getAuthenticationManager().authenticate(authRequest);
頂級身份管理者AuthenticationManager
TokenEndpoint。
Token處理端點TokenEndpoint
前面的兩個ClientCredentialsTokenEndpointFilter和AuthenticationManager可以理解為一些前置校驗,和身份封裝
@FrameworkEndpoint public class TokenEndpoint extends AbstractEndpoint { @RequestMapping(value = "/oauth/token", method=RequestMethod.POST) public ResponseEntity<OAuth3AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException { ... String clientId = getClientId(principal); ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId); //加載客戶端信息 ... TokenRequest tokenRequest = getOAuth3RequestFactory().createTokenRequest(parameters, authenticatedClient); //結(jié)合請求信息,創(chuàng)建TokenRequest ... OAuth3AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest); //將TokenRequest傳遞給TokenGranter頒發(fā)token ... return getResponse(token); } private TokenGranter tokenGranter;
真正的/oauth/token端點,其中方法參數(shù)中的Principal經(jīng)過之前的過濾器,已經(jīng)被填充了相關(guān)的信息,而方法的內(nèi)部則是依賴了一個TokenGranter 來頒發(fā)token。其中OAuth3AccessToken的實現(xiàn)類DefaultOAuth3AccessToken就是最終在控制臺得到的token序列化之前的原始類:
public class DefaultOAuth3AccessToken implements Serializable, OAuth3AccessToken { private static final long serialVersionUID = 914967629530462926L; private String value; private Date expiration; private String tokenType = BEARER_TYPE.toLowerCase(); private OAuth3RefreshToken refreshToken; private Set<String> scope; private Map<String, Object> additionalInformation = Collections.emptyMap(); //getter,setter } @org.codehaus.jackson.map.annotate.JsonSerialize(using = OAuth3AccessTokenJackson1Serializer.class) @org.codehaus.jackson.map.annotate.JsonDeserialize(using = OAuth3AccessTokenJackson1Deserializer.class) @com.fasterxml.jackson.databind.annotation.JsonSerialize(using = OAuth3AccessTokenJackson2Serializer.class) @com.fasterxml.jackson.databind.annotation.JsonDeserialize(using = OAuth3AccessTokenJackson2Deserializer.class) public interface OAuth3AccessToken { public static String BEARER_TYPE = "Bearer"; public static String OAUTH2_TYPE = "OAuth3"; /** * The access token issued by the authorization server. This value is REQUIRED. */ public static String ACCESS_TOKEN = "access_token"; /** * The type of the token issued as described in <a * href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-7.1">Section 7.1</a>. Value is case insensitive. * This value is REQUIRED. */ public static String TOKEN_TYPE = "token_type"; /** * The lifetime in seconds of the access token. For example, the value "3600" denotes that the access token will * expire in one hour from the time the response was generated. This value is OPTIONAL. */ public static String EXPIRES_IN = "expires_in"; /** * The refresh token which can be used to obtain new access tokens using the same authorization grant as described * in <a href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-6">Section 6</a>. This value is OPTIONAL. */ public static String REFRESH_TOKEN = "refresh_token"; /** * The scope of the access token as described by <a * href="http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-3.3">Section 3.3</a> */ public static String SCOPE = "scope"; ...
TokenGranter
TokenGranter的設(shè)計思路是使用CompositeTokenGranter管理一個List列表,每一種grantType對應(yīng)一個具體的真正授權(quán)者,在debug過程中可以發(fā)現(xiàn)CompositeTokenGranter 內(nèi)部就是在循環(huán)調(diào)用五種TokenGranter實現(xiàn)類的grant方法,而granter內(nèi)部則是通過grantType來區(qū)分是否是各自的授權(quán)類型。
public class CompositeTokenGranter implements TokenGranter { private final List<TokenGranter> tokenGranters; public CompositeTokenGranter(List<TokenGranter> tokenGranters) { this.tokenGranters = new ArrayList<TokenGranter>(tokenGranters); } public OAuth3AccessToken grant(String grantType, TokenRequest tokenRequest) { for (TokenGranter granter : tokenGranters) { OAuth3AccessToken grant = granter.grant(grantType, tokenRequest); if (grant!=null) { return grant; } } return null; } }
五種類型分別是:
ResourceOwnerPasswordTokenGranter ==> password密碼模式
AuthorizationCodeTokenGranter ==> authorization_code授權(quán)碼模式
ClientCredentialsTokenGranter ==> client_credentials客戶端模式
ImplicitTokenGranter ==> implicit簡化模式
RefreshTokenGranter ==>refresh_token 刷新token專用
以客戶端模式為例,思考如何產(chǎn)生token的,則需要繼續(xù)研究5種授權(quán)者的抽象類:AbstractTokenGranter
public abstract class AbstractTokenGranter implements TokenGranter { protected final Log logger = LogFactory.getLog(getClass()); //與token相關(guān)的service,重點 private final AuthorizationServerTokenServices tokenServices; //與clientDetails相關(guān)的service,重點 private final ClientDetailsService clientDetailsService; //創(chuàng)建oauth3Request的工廠,重點 private final OAuth3RequestFactory requestFactory; private final String grantType; ... public OAuth3AccessToken grant(String grantType, TokenRequest tokenRequest) { ... String clientId = tokenRequest.getClientId(); ClientDetails client = clientDetailsService.loadClientByClientId(clientId); validateGrantType(grantType, client); logger.debug("Getting access token for: " + clientId); return getAccessToken(client, tokenRequest); } protected OAuth3AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) { return tokenServices.createAccessToken(getOAuth3Authentication(client, tokenRequest)); } protected OAuth3Authentication getOAuth3Authentication(ClientDetails client, TokenRequest tokenRequest) { OAuth3Request storedOAuth3Request = requestFactory.createOAuth3Request(client, tokenRequest); return new OAuth3Authentication(storedOAuth3Request, null); } ... }
AuthorizationServerTokenServices分析重點
AuthorizationServer端的token操作service,接口設(shè)計如下:
public interface AuthorizationServerTokenServices { //創(chuàng)建token OAuth3AccessToken createAccessToken(OAuth3Authentication authentication) throws AuthenticationException; //刷新token OAuth3AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest) throws AuthenticationException; //獲取token OAuth3AccessToken getAccessToken(OAuth3Authentication authentication); }
在默認(rèn)的實現(xiàn)類DefaultTokenServices中,可以看到token是如何產(chǎn)生的,并且了解了框架對token進(jìn)行哪些信息的關(guān)聯(lián)。
@Transactional public OAuth3AccessToken createAccessToken(OAuth3Authentication authentication) throws AuthenticationException { OAuth3AccessToken existingAccessToken = tokenStore.getAccessToken(authentication); OAuth3RefreshToken refreshToken = null; if (existingAccessToken != null) { if (existingAccessToken.isExpired()) { if (existingAccessToken.getRefreshToken() != null) { refreshToken = existingAccessToken.getRefreshToken(); // The token store could remove the refresh token when the // access token is removed, but we want to // be sure... tokenStore.removeRefreshToken(refreshToken); } tokenStore.removeAccessToken(existingAccessToken); } else { // Re-store the access token in case the authentication has changed tokenStore.storeAccessToken(existingAccessToken, authentication); return existingAccessToken; } } // Only create a new refresh token if there wasn't an existing one // associated with an expired access token. // Clients might be holding existing refresh tokens, so we re-use it in // the case that the old access token // expired. if (refreshToken == null) { refreshToken = createRefreshToken(authentication); } // But the refresh token itself might need to be re-issued if it has // expired. else if (refreshToken instanceof ExpiringOAuth3RefreshToken) { ExpiringOAuth3RefreshToken expiring = (ExpiringOAuth3RefreshToken) refreshToken; if (System.currentTimeMillis() > expiring.getExpiration().getTime()) { refreshToken = createRefreshToken(authentication); } } OAuth3AccessToken accessToken = createAccessToken(authentication, refreshToken); tokenStore.storeAccessToken(accessToken, authentication); // In case it was modified refreshToken = accessToken.getRefreshToken(); if (refreshToken != null) { tokenStore.storeRefreshToken(refreshToken, authentication); } return accessToken; }
AuthorizationServerTokenServices的作用,他提供了創(chuàng)建token,刷新token,獲取token的實現(xiàn)。在創(chuàng)建token時,他會調(diào)用tokenStore對產(chǎn)生的token和相關(guān)信息存儲到對應(yīng)的實現(xiàn)類中,可以是redis,數(shù)據(jù)庫,內(nèi)存,jwt。
上述就是小編為大家分享的怎么看待Spring security oauth2的認(rèn)證流程了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。