Spring Security OAuth2 授權碼模式


 背景:

    由於業務實現中涉及到接入第三方系統(app接入有贊商城等),所以涉及到第三方系統需要獲取用戶信息(用戶手機號、姓名等),為了保證用戶信息的安全和接入方式的統一,

采用Oauth2四種模式之一的授權碼模式。

 介紹:

      

  • 第三方系統調用我方提供的授權接口(步驟1)
  • 用戶同意授權,后跳轉第三方系統(步驟2、3)
  • 第三方系統獲得code,根據code到我方系統獲取token(步驟5、6 )
  • 根據獲取token訪問受保護的資源(步驟8、9)

     實際應用中由於合作商戶,所以需要直接返回code,不需要用戶手動授權,即靜默模式,所以需要擴展框架,使其支持自動授權

擴展:

     項目使用的是spring-security-oauth2-2.0.15 由於默認情況下,需要用戶授權通過才能生成授權碼。所以需簡要對框架進行擴展

(1)spring-security-oauth2獲取code的controller:

 1 RequestMapping(value = "/oauth/authorize")
 2     public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters,
 3             SessionStatus sessionStatus, Principal principal) {
 4 
 5         // Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
 6         // query off of the authorization request instead of referring back to the parameters map. The contents of the
 7         // parameters map will be stored without change in the AuthorizationRequest object once it is created.
 8         AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
 9 
10         Set<String> responseTypes = authorizationRequest.getResponseTypes();
11 
12         if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
13             throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
14         }
15 
16         if (authorizationRequest.getClientId() == null) {
17             throw new InvalidClientException("A client id must be provided");
18         }
19 
20         try {
21 
22             if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
23                 throw new InsufficientAuthenticationException(
24                         "User must be authenticated with Spring Security before authorization can be completed.");
25             }
26 
27             ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
28 
29             // The resolved redirect URI is either the redirect_uri from the parameters or the one from
30             // clientDetails. Either way we need to store it on the AuthorizationRequest.
31             String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
32             String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);
33             if (!StringUtils.hasText(resolvedRedirect)) {
34                 throw new RedirectMismatchException(
35                         "A redirectUri must be either supplied or preconfigured in the ClientDetails");
36             }
37             authorizationRequest.setRedirectUri(resolvedRedirect);
38 
39             // We intentionally only validate the parameters requested by the client (ignoring any data that may have
40             // been added to the request by the manager).
41             oauth2RequestValidator.validateScope(authorizationRequest, client);
42 
43             // Some systems may allow for approval decisions to be remembered or approved by default. Check for
44             // such logic here, and set the approved flag on the authorization request accordingly.
45             authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
46                     (Authentication) principal);
47             // TODO: is this call necessary?
48             boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
49             authorizationRequest.setApproved(approved);
50 
51             // Validation is all done, so we can check for auto approval...
52             if (authorizationRequest.isApproved()) {
53                 if (responseTypes.contains("token")) {
54                     return getImplicitGrantResponse(authorizationRequest);
55                 }
56                 if (responseTypes.contains("code")) {
57                     return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
58                             (Authentication) principal));
59                 }
60             }
61 
62             // Place auth request into the model so that it is stored in the session
63             // for approveOrDeny to use. That way we make sure that auth request comes from the session,
64             // so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
65             model.put("authorizationRequest", authorizationRequest);
66 
67             return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
68 
69         }
70         catch (RuntimeException e) {
71             sessionStatus.setComplete();
72             throw e;
73         }
74 
75     }

52行到59行可知, 當approved 為true的時候會直接返回code碼,不會需要用戶授權,所以問題變成了如何讓擴展的userApprovalHandler生效

(2)擴展的userApprovalHandler生效

 1   @Override
 2   public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
 3     endpoints
 4         .tokenStore(tokenStore)
 5         .authenticationManager(authenticationManager)
 6         .userDetailsService(authUserDetailService)
 7         .authorizationCodeServices(new JdbcAuthorizationCodeServices(dataSource))
 8         .reuseRefreshTokens(false)
 9         .userApprovalHandler(new AuthApprovalHandler())
10         .exceptionTranslator(customWebResponseExceptionTranslator)
11     ;
12   }

   9行:AuthotizationServer中增加配置自定義配置

應用:

(1)獲取code值

   需要APP端定制webview開發,根據/oauth/authrorize路徑參數中增加token

  請求路徑: 

  參數說明:

參數名稱
類型
是否必填
描述
response_type
String 固定值“code”
client_id
String 第三方配置的client_id
redirect_uri
String 第三方配置的回調地址
     state String 第三方自定義使用


  請求示例:

curl -X POST http://localhost:8421/oauth/authorize -H 'Authorization: Bearer b7c2d63f-edff-4790-add9-0b69df7321b5' -d 'response_type=code&client_id=external&redirect_uri=http://www.baidu.com&state=123'

   返回結果:  重定向redirect_uri路徑

  (2)獲取accessToken(有效期暫定72h

  請求參數:

參數名稱
類型
是否必填
描述
client_id
String 第三方配置的client_id
client_secret
String 第三方配置的密鑰
    code String 申請的code
grant_type
String 固定值“authorization_code”
redirect_uri
String 第三方配置的回調地址,必須與生成code時的uri一樣

  請求示例:

curl -X POST http://localhost:8421/oauth/token -d 'grant_type=authorization_code&client_id=external&client_secret=D524C1A0811DA49592F841085CC0063EB62B3001252A94542795D1CA9824A941&redirect_uri=http://www.baidu.com&code=4TCYkV'

  返回結果:

{"access_token":"95b5be18-49a3-44e1-a527-d5da036cfc3f","token_type":"bearer","refresh_token":"b4488c7d-1e8c-4317-a955-1f4bda013a35","expires_in":9891370,"scope":"auth_base"}

  (3) 獲取refreshToken

      暫時不支持

  (4) 訪問資源(用戶信息)

      根據獲取到的授權token訪問用戶資源信息

     請求示例:

curl -X POST https://wuxi.test.brightcns.cn/api/v2/user/external/info -H 'Authorization: Bearer e86d752e-8d72-4a33-aa98-8e158ac5b50b'

     返回結果:

{"success":true,"msg":"success","code":"SUCCESS","data":{"userId":4738295200051366773,"phone":13916413714,"nickname":"13916413714","avatar":"http://wxcardoss.oss-cn-shanghai.aliyuncs.com/null","realName":null,"extendParam":null}}

 

 

 

 

    

 

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM