当前位置:   article > 正文

Oauth2--- 授权码模式(authorization_code)过程

authorization_code

一、获取授权码Code:

访问授权服务器 /oauth/authorize 端点:(只用于"implicit", "authorization_code")

GET: http://127.0.0.1:8080/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://www.baidu.com

参数:

(1)response_type=code  必须指定。

  (2) client_id=client  客户端必须指定。

(3)redirect_uri=http:// 重定向地址 必须与数据表一致


结果:如果同意授权: 返回code=cown42 并跳转到redirect_uri=http://www.baidu.com

过程:比较client_id、及redirect_uri、scope、

涉及到核心类:

(1)DefaultRedirectResolver:redirect_uri地址校验。

(2)JdbcAuthorizationCodeServices/InMemoryAuthorizationCodeServices: 生成的code,并保存(code,OAuth2Authentication)在内存或数据库(oauth_code表)

(3)JdbcClientDetailsService: 从表oauth_client_details读取ClientDetails信息,用来校验。

(4)LoginUrlAuthenticationEntryPoint:末登录时,异常由ExceptionTranslationFilter doFilter()---》LoginUrlAuthenticationEntryPoint--》重定向到loginFromUrl页面。

 

  1. public class AuthorizationEndpoint extends AbstractEndpoint {
  2. //一、相关操作:
  3. private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
  4. private RedirectResolver redirectResolver = new DefaultRedirectResolver();
  5. private UserApprovalHandler userApprovalHandler = new DefaultUserApprovalHandler();
  6. private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
  7. private OAuth2RequestValidator oauth2RequestValidator = new DefaultOAuth2RequestValidator();
  8. //授权确认页面
  9. private String userApprovalPage = "forward:/oauth/confirm_access";
  10. private String errorPage = "forward:/oauth/error";
  11. //二、接口:
  12. @RequestMapping(value = "/oauth/authorize")
  13. public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters,SessionStatus sessionStatus, Principal principal) {
  14. /*1. 通过DefaultOAuth2RequestFactory.createAuthorizationRequest()
  15. AuthorizationRequest request = new AuthorizationRequest();
  16. 从client数据库表加载clietDetails信息
  17. ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
  18. request.setResourceIdsAndAuthoritiesFromClientDetails(clientDetails);
  19. */
  20. AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
  21. Set<String> responseTypes = authorizationRequest.getResponseTypes();
  22. //2.反应类型必须是code
  23. if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
  24. throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
  25. }
  26. //3.必须指定clientId
  27. if (authorizationRequest.getClientId() == null) {
  28. throw new InvalidClientException("A client id must be provided");
  29. }
  30. try {
  31. //4.访问该端点必须认证isAuthenticated = ture,抛出异常,由ExceptionTranslationFilter doFilter()---》LoginUrlAuthenticationEntryPoint--》重定向到loginFromUrl页面。
  32. if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
  33. throw new InsufficientAuthenticationException(
  34. "User must be authenticated with Spring Security before authorization can be completed.");
  35. }
  36. //5.从数据库加载clientDetails
  37. ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
  38. //6. 必须指定redirect_uri 而且与数据库里的设置一至。同时认证类型只能:"implicit", "authorization_code"
  39. String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
  40. String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
  41. if (!StringUtils.hasText(resolvedRedirect)) {
  42. throw new RedirectMismatchException(
  43. "A redirectUri must be either supplied or preconfigured in the ClientDetails");
  44. }
  45. authorizationRequest.setRedirectUri(resolvedRedirect);
  46. // 7。访问范畴Scope比对
  47. oauth2RequestValidator.validateScope(authorizationRequest, client);
  48. // 8.是否受权
  49. authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
  50. (Authentication) principal);
  51. // TODO: is this call necessary?
  52. boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
  53. authorizationRequest.setApproved(approved);
  54. // 10.授权通过后,直接返回数据。
  55. // (1) Code授权:生成code,同时保存在InMemoryAuthorizationCodeServices(默认)或JdbcAuthorizationCodeServices(表oauth_code)
  56. if (authorizationRequest.isApproved()) {
  57. if (responseTypes.contains("token")) {
  58. return getImplicitGrantResponse(authorizationRequest);
  59. }
  60. if (responseTypes.contains("code")) {
  61. return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
  62. (Authentication) principal));
  63. }
  64. }
  65. //11.弹窗口,让用户授权
  66. model.put("authorizationRequest", authorizationRequest);
  67. return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
  68. }
  69. catch (RuntimeException e) {
  70. sessionStatus.setComplete();
  71. throw e;
  72. }
  73. }

二、 获取访问令牌: 访问授权服务器 /oauth/token 端点

确保用户是已经登陆的情况,返回access_token格式.

  1. Post:
  2. http://localhost:8080/oauth/token?
  3. grant_type=authorization_code&code=o4YrCS&client_id=pair &client_secret=secret&redirect_uri=http://baidu.com

参数:

(1) clientId: 客户端id,参数指定同时Principal也必须一致。否则 InvalidClientException(必须)

(2)client_secret:客户端密匙

(2)grant_type:授权类型。(必须)

(3)code: 授权码(必须)

核心类:

(1)AuthorizationServerTokenServices/DefaultTokenServices:由这类产生、刷新、获取token.

  (2) TokenStore:由用户自己提供实现代码。

(3)类包装关系: AbstractEndpoint----》AuthorizationServerTokenServices(DefaultTokenServices)---》TokenStore

  1. @FrameworkEndpoint
  2. public class TokenEndpoint extends AbstractEndpoint {
  3. private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();
  4. /**
  5. * 获取access_token
  6. **/
  7. @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
  8. public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
  9. Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
  10. //1.必须是认证通过的用户,否则会抛出异常,异常由ExceptionTranslationFilter doFilter()---》LoginUrlAuthenticationEntryPoint--》重定向到loginFromUrl页面。
  11. if (!(principal instanceof Authentication)) {
  12. throw new InsufficientAuthenticationException(
  13. "There is no client authentication. Try adding an appropriate authentication filter.");
  14. }
  15. //2.读取数据表加载ClientDetails信息。
  16. String clientId = getClientId(principal);
  17. ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
  18. TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
  19. //2.检查clientId是否正确
  20. if (clientId != null && !clientId.equals("")) {
  21. if (!clientId.equals(tokenRequest.getClientId())) {
  22. throw new InvalidClientException("Given client ID does not match authenticated client");
  23. }
  24. }
  25. //3.检查Scope
  26. if (authenticatedClient != null) {
  27. oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
  28. }
  29. //4.检查GrantType: 且不能为implicit
  30. if (!StringUtils.hasText(tokenRequest.getGrantType())) {
  31. throw new InvalidRequestException("Missing grant type");
  32. }
  33. if (tokenRequest.getGrantType().equals("implicit")) {
  34. throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
  35. }
  36. //5.如果是authorization_code类型,检查AuthCode:AuthCode != null && grant_type = authorization_code
  37. if (isAuthCodeRequest(parameters)) {
  38. // The scope was requested or determined during the authorization step
  39. if (!tokenRequest.getScope().isEmpty()) {
  40. logger.debug("Clearing scope of incoming token request");
  41. tokenRequest.setScope(Collections.<String> emptySet());
  42. }
  43. }
  44. // 授权是刷新token: grant_type = refresh_token
  45. if (isRefreshTokenRequest(parameters)) {
  46. // A refresh token has its own default scopes, so we should ignore any added by the factory here.
  47. tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
  48. }
  49. //5.生成 token,TokenGranter(用户指定)生成一个OAuth2AccessToken。
  50. OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
  51. if (token == null) {
  52. throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
  53. }
  54. return getResponse(token);
  55. }
  1. public abstract class AbstractTokenGranter implements TokenGranter {
  2. /**
  3. * 核心类:提供token所有相关操作(最终由DefaultTokenServices实现类实现功能)
  4. **/
  5. private final AuthorizationServerTokenServices tokenServices;
  6. private final ClientDetailsService clientDetailsService;
  7. private final OAuth2RequestFactory requestFactory;
  8. private final String grantType;
  9. }
  1. OAuth2AccessToken {
  2. public static String BEARER_TYPE = "Bearer";
  3. public static String OAUTH2_TYPE = "OAuth2";
  4. public static String ACCESS_TOKEN = "access_token";
  5. public static String TOKEN_TYPE = "token_type";
  6. public static String EXPIRES_IN = "expires_in";
  7. public static String REFRESH_TOKEN = "refresh_token";
  8. public static String SCOPE = "scope";
  9. }
  1. /**
  2. * 1.TokenStore:核心类,所有token相关的操作由它提供
  3. * 2.核心功能:(1)提供accessToken 的创建、刷新服务 (2)loadAuthentication()accessToken获取用户信息过程
  4. * 3.核心类:
  5. * (1)AuthorizationServerTokenServices:授权服务器的提供accessToken 的创建、刷新服务。
  6. * (2)ResourceServerTokenServices:授权服务器的提供accessToken 获取用户信息,提供给资源服务器去tokenString---> OAuth2Authentication
  7. */
  8. public class DefaultTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,
  9. ConsumerTokenServices, InitializingBean {
  10. //Token生成、保存服务。(核心重点)
  11. private TokenStore tokenStore;
  12. //ClientDetail 服务
  13. private ClientDetailsService clientDetailsService;
  14. //Token信息添加
  15. private TokenEnhancer accessTokenEnhancer;
  16. //认证管理:当token刷新时,用于重新认证
  17. private AuthenticationManager authenticationManager;
  18. /**
  19. * 生成 OAuth2AccessToken
  20. */
  21. @Transactional
  22. public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
  23. //1. 直接从tokenStore服务获取accessToken
  24. OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
  25. OAuth2RefreshToken refreshToken = null;
  26. //1.tokenStore有accessToken时,检查是否失效。
  27. if (existingAccessToken != null) {
  28. if (existingAccessToken.isExpired()) {
  29. if (existingAccessToken.getRefreshToken() != null) {
  30. refreshToken = existingAccessToken.getRefreshToken();
  31. tokenStore.removeRefreshToken(refreshToken);
  32. }
  33. tokenStore.removeAccessToken(existingAccessToken);
  34. }
  35. else {
  36. tokenStore.storeAccessToken(existingAccessToken, authentication);
  37. return existingAccessToken;
  38. }
  39. }
  40. //2.生成 accessToken 和 refreshToken 并保存
  41. refreshToken = createRefreshToken(authentication);
  42. OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
  43. tokenStore.storeAccessToken(accessToken, authentication);
  44. tokenStore.storeRefreshToken(refreshToken, authentication);
  45. return accessToken;
  46. }
  47. /**
  48. * 刷新aceessToken
  49. */
  50. @Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
  51. public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
  52. throws AuthenticationException {
  53. //1. 刷新token
  54. OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
  55. //2.重新认证
  56. OAuth2Authentication authentication = tokenStore.readAuthenticationForRefreshToken(refreshToken);
  57. //3.重新生成、保存token
  58. refreshToken = createRefreshToken(authentication);
  59. OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
  60. tokenStore.storeAccessToken(accessToken, authentication);
  61. tokenStore.storeRefreshToken(accessToken.getRefreshToken(), authentication);
  62. return accessToken;
  63. }
  64. /**
  65. * 从accessToken 获取到OAuth2Authentication
  66. */
  67. public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException,
  68. InvalidTokenException {
  69. OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
  70. OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
  71. //校验
  72. if (clientDetailsService != null) {
  73. String clientId = result.getOAuth2Request().getClientId();
  74. try {
  75. clientDetailsService.loadClientByClientId(clientId);
  76. }
  77. catch (ClientRegistrationException e) {
  78. throw new InvalidTokenException("Client not valid: " + clientId, e);
  79. }
  80. }
  81. return result;
  82. }
  83. /**
  84. * 生成RefreshToken就是简单的json字符串
  85. */
  86. private OAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
  87. // OAuth2RefreshToken数据结构: @JsonValue String getValue();
  88. return new DefaultOAuth2RefreshToken(value);
  89. }
  90. /**
  91. * 生成 OAuth2AccessToken
  92. */
  93. private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
  94. //生成AccessToken
  95. /**OAuth2AccessToken数据结构
  96. private String value;
  97. private Date expiration;
  98. private String tokenType = BEARER_TYPE.toLowerCase();
  99. private OAuth2RefreshToken refreshToken;
  100. private Set<String> scope;
  101. private Map<String, Object> additionalInformation = Collections.emptyMap();
  102. */
  103. DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
  104. token.setRefreshToken(refreshToken);
  105. token.setScope(authentication.getOAuth2Request().getScope());
  106. //调用用户指定的accessTokenEnhancer进行数据插入
  107. return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
  108. }
  109. }

 

三、访问资源服务器受保护的资源:

根据access_token获取资源, 附上令牌在请求头,**需加上 Bearer **

访问http://localhost:8080/rest/api/ping?access_token=a8ae6a78-289d-4594-a421-9b56aa8f7213

(1)PostMan工具: GET/POST: /xxx/xxxx Header: Authorization Bearer access_token

(2)Curl命令: curl -X GET \ http://localhost:9001/test \ -i -H "Accept: application/json" -H "Authorization: Bearer eyJhbG" \

 

四、配置AuthorizationServerEndpointsConfigurer:

主要注入想着的ServerBean。

  1. /**
  2. * 配置token的保存方式
  3. * 1. 密码模式下配置认证管理器 AuthenticationManager
  4. * 2. 设置 AccessToken的存储介质tokenStore, 默认使用内存当做存储介质。
  5. */
  6. @Override
  7. public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
  8. TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
  9. endpoints
  10. .authenticationManager(authenticationManager)
  11. .userDetailsService(userDetailsService) //MUST:密码模式下需设置一个AuthenticationManager对象,获取 UserDetails信息
  12. .tokenStore(tokenStore)//token的保存方式
  13. .tokenEnhancer(tokenEnhancerChain);//token里加点信息
  14. }
  15. //所有Endpoints所用的到服务都在这里注入完成,保存在这个对象
  16. public final class AuthorizationServerEndpointsConfigurer {
  17. private AuthorizationServerTokenServices tokenServices;
  18. private ConsumerTokenServices consumerTokenServices;
  19. private AuthorizationCodeServices authorizationCodeServices;
  20. private ResourceServerTokenServices resourceTokenServices;
  21. private TokenStore tokenStore;
  22. private TokenEnhancer tokenEnhancer;
  23. private AccessTokenConverter accessTokenConverter;
  24. private ApprovalStore approvalStore;
  25. private TokenGranter tokenGranter;
  26. private OAuth2RequestFactory requestFactory;
  27. private OAuth2RequestValidator requestValidator;
  28. private UserApprovalHandler userApprovalHandler;
  29. private AuthenticationManager authenticationManager;
  30. private ClientDetailsService clientDetailsService;
  31. private String prefix;
  32. private Map<String, String> patternMap = new HashMap<String, String>();
  33. private Set<HttpMethod> allowedTokenEndpointRequestMethods = new HashSet<HttpMethod>();
  34. private FrameworkEndpointHandlerMapping frameworkEndpointHandlerMapping;
  35. private boolean approvalStoreDisabled;
  36. private List<Object> interceptors = new ArrayList<Object>();
  37. private DefaultTokenServices defaultTokenServices;
  38. private UserDetailsService userDetailsService;
  39. private boolean tokenServicesOverride = false;
  40. private boolean userDetailsServiceOverride = false;
  41. private boolean reuseRefreshToken = true;
  42. private WebResponseExceptionTranslator<OAuth2Exception> exceptionTranslator;
  43. private RedirectResolver redirectResolver;
  44. }

 

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/84066
推荐阅读
相关标签
  

闽ICP备14008679号