本文主要对从用户发起获取token的请求(/oauth/token),到请求结束返回token中间经过的几个关键点进行说明。
注:所有请求均为post请求。
http://localhost/oauth/token?client_id=demoClientId&client_secret=demoClientSecret&grant_type=password&username=demoUser&password=50575tyL86xp29O380t1
http://localhost/oauth/check_token?token=f57ce129-2d4d-4bd7-1111-f31ccc69d4d1
http://localhost/oauth/token?grant_type=refresh_token&refresh_token=fbde81ee-f419-42b1-1234-9191f1f95be9&client_id=demoClientId&client_secret=demoClientSecret
注:文中介绍的认证服务器端token存储在Reids,用户信息存储使用数据库,文中会包含相关的部分代码。
加粗内容为每一步的重点,不想细看的可以只看加粗内容:
刷新token(refresh token)的流程与获取token的流程只有⑨有所区别:
tokenStore通常情况为自定义实现,一般放置在缓存或者数据库中。此处可以利用自定义tokenStore来实现多种需求,如:
1.一个比较重要的过滤器 2.此处是①中的attemptAuthentication方法 3.此处是②中调用的authenticate方法 4.此处是③中调用的AbstractUserDetailsAuthenticationProvider类的authenticate方法 5.此处是④中调用的DaoAuthenticationProvider类的retrieveUser方法 6.此处为⑤中调用的ClientDetailsUserDetailsService类的loadUserByUsername方法,执行完后接着返回执行④之后的方法 7.此处为④中调用的DaoAuthenticationProvider类的additionalAuthenticationChecks方法,此处执行完则主要过滤器执行完毕,后续会进入/oauth/token映射的方法。 8.此处进入/oauth/token映射的TokenEndpoint类的postAccessToken方法 9.此处为⑧中调用的AbstractTokenGranter类的grant方法 10.此处为⑨中调用的ResourceOwnerPasswordTokenGranter类中的getOAuth2Authentication方法 11.此处为⑩中调用的自定义的CustomUserAuthenticationProvider类中的authenticate方法,此处校验用户密码是否正确,此处执行完则返回⑨执行后续方法。 12.此处为⑨中调用的DefaultTokenServices中的createAccessToken方法 13.此处为12中调用的RedisTokenStore中的getAccessToken方法等,此处执行完,则一直向上返回到⑧中执行后续方法。 14.此处为⑧中获取到token后需要包装返回流操作
<!-- 认证地址 --> <sec:http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="authenticationManager" > <sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" /> <sec:anonymous enabled="false" /> <sec:http-basic entry-point-ref="clientAuthenticationEntryPoint" /> <sec:custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" /> <sec:access-denied-handler ref="oauthAccessDeniedHandler" /> </sec:http> <bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint"> <property name="realmName" value="springsec/client" /> <property name="typeName" value="Basic" /> </bean> <bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter"> <property name="authenticationManager" ref="authenticationManager" /> </bean> <bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler"> </bean> <!-- 认证管理器--> <sec:authentication-manager alias="authenticationManager"> <sec:authentication-provider user-service-ref="clientDetailsUserService" /> </sec:authentication-manager> <!-- 注入自定义clientDetails--> <bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService"> <constructor-arg ref="clientDetails" /> </bean> <!-- 自定义clientDetails--> <bean id="clientDetails" class="com.xxx.core.framework.oauth.CustomClientDetailsServiceImpl"> </bean> <!-- 注入自定义provider--> <sec:authentication-manager id="userAuthenticationManager"> <sec:authentication-provider ref="customUserAuthenticationProvider" /> </sec:authentication-manager> <!--自定义用户认证provider--> <bean id="customUserAuthenticationProvider" class="com.xxx.core.framework.oauth.CustomUserAuthenticationProvider"> </bean> <oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices" check-token-enabled="true" > <oauth:authorization-code /> <oauth:implicit/> <oauth:refresh-token/> <oauth:client-credentials /> <oauth:password authentication-manager-ref="userAuthenticationManager"/> </oauth:authorization-server> <!-- 自定义tokenStore--> <bean id="tokenStore" class="com.xxx.core.framework.oauth.RedisTokenStore" /> <!-- 设置access_token有效期,设置支持refresh_token,refresh_token有效期默认为30天--> <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices"> <property name="tokenStore" ref="tokenStore" /> <property name="supportRefreshToken" value="true" /> <property name="accessTokenValiditySeconds" value="43200"></property> <property name="clientDetailsService" ref="clientDetails" /> </bean>
本文中的流程能够结果的问题:
PS:梳理这个认证流程也是因为最近工作需要设置token超时机制,刷新token,检查token等,需要对认证这块了解的特别清楚才行,后面的代码截图只是详细的介绍了获取access_token的流程,其实refresh_token与access_token的流程基本是一样的,如果您在使用refresh_token过程中有什么问题,也可以详细看下上面的截图,或许会有一些收获。