千家信息网

怎么看待Spring security oauth2的认证流程

发表于:2024-11-11 作者:千家信息网编辑
千家信息网最后更新 2024年11月11日,这期内容当中小编将会给大家带来有关怎么看待Spring security oauth2的认证流程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Spring Secu
千家信息网最后更新 2024年11月11日怎么看待Spring security oauth2的认证流程

这期内容当中小编将会给大家带来有关怎么看待Spring security oauth2的认证流程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。

Spring Security OAuth3 整理

  1. 隐式授权模式(Implicit Grant)

需要提供的参数    地址:          oauth/token    请求头参数:        Authorization=Basic base64.encode(client_id:client_secret)     ==如果secret为空,只对client_id进行编码==         POST参数      grant_type:password      username: 用户名     password:密码
  1. 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: 作用域
  1. 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. 授权码模式

      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

Spring Security OAuth3 认证流程

  1. 首先开启@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    }
  1. 客户端身份认证核心过滤器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);
  1. 顶级身份管理者AuthenticationManager

从请求中获取client_id,client_secret,组装成一个UsernamePasswordAuthenticationToken作为身份标识,使用容器中的顶级身份管理器AuthenticationManager去进行身份认证(AuthenticationManager的实现类一般是ProviderManager。而ProviderManager内部维护了一个List,真正的身份认证是由一系列AuthenticationProvider去完成。而AuthenticationProvider的常用实现类则是DaoAuthenticationProvider,DaoAuthenticationProvider内部又聚合了一个UserDetailsService接口,UserDetailsService才是获取用户详细信息的最终接口

client模式是不存在"用户"的概念的,那么这里的身份认证是在认证什么呢?debug可以发现UserDetailsService的实现被适配成了ClientDetailsUserDetailsService,这个设计是将client客户端的信息(client_id,client_secret)适配成用户的信息(username,password)经过ClientCredentialsTokenEndpointFilter之后,身份信息已经得到了AuthenticationManager的验证。接着便到达了

TokenEndpoint。

  1. Token处理端点TokenEndpoint

前面的两个ClientCredentialsTokenEndpointFilter和AuthenticationManager可以理解为一些前置校验,和身份封装

@FrameworkEndpointpublic class TokenEndpoint extends AbstractEndpoint {     @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)    public ResponseEntity postAccessToken(Principal principal, @RequestParam    Map 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 scope;     private Map 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 Section 7.1. 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 Section 6. This value is OPTIONAL.     */    public static String REFRESH_TOKEN = "refresh_token";    /**     * The scope of the access token as described by Section 3.3     */    public static String SCOPE = "scope";    ...
  1. TokenGranter

TokenGranter的设计思路是使用CompositeTokenGranter管理一个List列表,每一种grantType对应一个具体的真正授权者,在debug过程中可以发现CompositeTokenGranter 内部就是在循环调用五种TokenGranter实现类的grant方法,而granter内部则是通过grantType来区分是否是各自的授权类型。

public class CompositeTokenGranter implements TokenGranter {     private final List tokenGranters;     public CompositeTokenGranter(List tokenGranters) {        this.tokenGranters = new ArrayList(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);    }     ...}
  1. 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进行哪些信息的关联。

@Transactionalpublic 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的认证流程了,如果刚好有类似的疑惑,不妨参照上述分析进行理解。如果想知道更多相关知识,欢迎关注行业资讯频道。

0