背景:java
因爲業務實現中涉及到接入第三方系統(app接入有贊商城等),因此涉及到第三方系統須要獲取用戶信息(用戶手機號、姓名等),爲了保證用戶信息的安全和接入方式的統一,web
採用Oauth2四種模式之一的受權碼模式。spring
介紹:api
實際應用中因爲合做商戶,因此須要直接返回code,不須要用戶手動受權,即靜默模式,因此須要擴展框架,使其支持自動受權安全
擴展:bash
項目使用的是spring-security-oauth2-2.0.15 因爲默認狀況下,須要用戶受權經過才能生成受權碼。因此需簡要對框架進行擴展session
(1)spring-security-oauth2獲取code的controller:app
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生效curl
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}}