这期内容当中小编将会给大家带来有关怎么看待Spring security oauth2的认证流程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。
隐式授权模式(Implicit Grant)
需要提供的参数 地址: oauth/token 请求头参数: Authorization=Basic base64.encode(client_id:client_secret) ==如果secret为空,只对client_id进行编码== POST参数 grant_type:password username: 用户名 password:密码
client_credentials 认证
需要传递的参数 地址: oauth/token Header参数 client_id: client_secret: Header头添加 Authorization=Basic base64.encode(client_id:client_secret) ==如果secret为空,只对client_id进行编码== Post参数 grant_type: password scope: 作用域
password 模式
需要传递的参数 地址: oauth/token Header参数 client_id: client_secret: Header头添加 Authorization=Basic base64.encode(client_id:client_secret) ==如果secret为空,只对client_id进行编码== Post参数 grant_type: password username: 用户名 password: 密码 scope: 作用域
授权码模式
1.首先请求oauth/authorize 获取code 需要提交的参数 Header参数 client_id: client_secret: Header头添加 Authorization=Basic base64.encode(client_id:client_secret) 如果secret为空,只对client_id进行编码 post参数 grant_type: password username: 用户名 password: 密码 scope: 作用域 2. 通过code 访问 oauth/token接口,换取回应的accessToken
首先开启@EnableAuthorizationServer 注解
AuthorizationServerConfigurerAdapterm默认方法配置
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer { @Override public void configure(AuthorizationServerSecurityConfigurer security ) throws Exception { // 配置AuthorizationServer安全认证的相关信息,创建ClientCredentialsTokenEndpointFilter核心过滤器 } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { //配置OAuth3的客户端相关信息 } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { //配置AuthorizationServerEndpointsConfigurer众多相关类,包括配置身份认证器,配置认证方式,TokenStore,TokenGranter,OAuth3RequestFactory }
客户端身份认证核心过滤器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); //结合请求信息,创建TokenRequest ... OAuth3AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest); //将TokenRequest传递给TokenGranter颁发token ... return getResponse(token); } private TokenGranter tokenGranter;
真正的/oauth/token端点,其中方法参数中的Principal经过之前的过滤器,已经被填充了相关的信息,而方法的内部则是依赖了一个TokenGranter 来颁发token。其中OAuth3AccessToken的实现类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的设计思路是使用CompositeTokenGranter管理一个List列表,每一种grantType对应一个具体的真正授权者,在debug过程中可以发现CompositeTokenGranter 内部就是在循环调用五种TokenGranter实现类的grant方法,而granter内部则是通过grantType来区分是否是各自的授权类型。
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授权码模式
ClientCredentialsTokenGranter ==> client_credentials客户端模式
ImplicitTokenGranter ==> implicit简化模式
RefreshTokenGranter ==>refresh_token 刷新token专用
以客户端模式为例,思考如何产生token的,则需要继续研究5种授权者的抽象类:AbstractTokenGranter
public abstract class AbstractTokenGranter implements TokenGranter { protected final Log logger = LogFactory.getLog(getClass()); //与token相关的service,重点 private final AuthorizationServerTokenServices tokenServices; //与clientDetails相关的service,重点 private final ClientDetailsService clientDetailsService; //创建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,接口设计如下:
public interface AuthorizationServerTokenServices { //创建token OAuth3AccessToken createAccessToken(OAuth3Authentication authentication) throws AuthenticationException; //刷新token OAuth3AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest) throws AuthenticationException; //获取token OAuth3AccessToken getAccessToken(OAuth3Authentication authentication); }
在默认的实现类DefaultTokenServices中,可以看到token是如何产生的,并且了解了框架对token进行哪些信息的关联。
@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的作用,他提供了创建token,刷新token,获取token的实现。在创建token时,他会调用tokenStore对产生的token和相关信息存储到对应的实现类中,可以是redis,数据库,内存,jwt。
上述就是小编为大家分享的怎么看待Spring security oauth2的认证流程了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。