一、oauth原理參考
二、本例中采用授權碼模式

大致流程
(A)用戶訪問客戶端,后者將前者導向認證服務器。
(B)用戶選擇是否給予客戶端授權。
(C)假設用戶給予授權,認證服務器將用戶導向客戶端事先指定的"重定向URI"(redirection URI),同時附上一個授權碼。
(D)客戶端收到授權碼,附上早先的"重定向URI",向認證服務器申請令牌。這一步是在客戶端的后台的服務器上完成的,對用戶不可見。
(E)認證服務器核對了授權碼和重定向URI,確認無誤后,向客戶端發送訪問令牌(access token)和更新令牌(refresh token)。
參數含義
response_type:表示授權類型,必選項,此處的值固定為"code"
client_id:表示客戶端的ID,必選項
redirect_uri:表示重定向URI,可選項
scope:表示申請的權限范圍,可選項,本例中無
state:表示客戶端的當前狀態,可以指定任意值,認證服務器會原封不動地返回這個值,本例中無
三、項目中依賴oauth相關jar
<!-- oauth --> <dependency> <groupId>org.apache.oltu.oauth2</groupId> <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId> <version>${oauth2-version}</version> </dependency> <dependency> <groupId>org.apache.oltu.oauth2</groupId> <artifactId>org.apache.oltu.oauth2.authzserver</artifactId> <version>${oauth2-version}</version> </dependency> <dependency> <groupId>org.apache.oltu.oauth2</groupId> <artifactId>org.apache.oltu.oauth2.client</artifactId> <version>${oauth2-version}</version> </dependency>
四、獲取授權碼
/** * 獲取授權碼-服務端 * * @param request * @return * @throws OAuthProblemException * @throws OAuthSystemException */ @RequestMapping(value = "/authorize", method = RequestMethod.GET) @ResponseBody public Object authorize(HttpServletRequest request) throws URISyntaxException, OAuthProblemException, OAuthSystemException { try { // 構建OAuth授權請求 OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request); // 1.獲取OAuth客戶端id String clientId = oauthRequest.getClientId(); // 校驗客戶端id是否正確 LightUserResult lightUserResult = userApi.queryUserByClientId(clientId); if (null == lightUserResult) { OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST) .setError(OAuthError.TokenResponse.INVALID_CLIENT) .setErrorDescription("無效的客戶端ID") .buildJSONMessage(); return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus())); } // 2.生成授權碼 String authCode = null; String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE); // ResponseType僅支持CODE和TOKEN if (responseType.equals(ResponseType.CODE.toString())) { OAuthIssuerImpl oAuthIssuer = new OAuthIssuerImpl(new MD5Generator()); authCode = oAuthIssuer.authorizationCode(); // 存入緩存中authCode-username RedisUtil.getRedis().set(authCode, lightUserResult.getUserName()); } return new ResponseEntity(authCode, HttpStatus.OK); } catch (Exception e) { return new ResponseEntity("內部錯誤", HttpStatus.valueOf(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); } }
五、根據授權碼獲取token
/** * 獲取訪問令牌 * * @param request * @return * @throws OAuthProblemException * @throws OAuthSystemException */ @RequestMapping(value = "accessToken", method = RequestMethod.POST) @ResponseBody public Object accessToken(HttpServletRequest request) throws OAuthProblemException, OAuthSystemException { try { // 構建OAuth請求 OAuthTokenRequest tokenRequest = new OAuthTokenRequest(request); // 1.獲取OAuth客戶端id String clientId = tokenRequest.getClientId(); // 校驗客戶端id是否正確 LightUserResult lightUserResult = userApi.queryUserByClientId(clientId); if (null == lightUserResult) { OAuthResponse oAuthResponse = OAuthResponse .errorResponse(HttpServletResponse.SC_BAD_REQUEST) .setError(OAuthError.TokenResponse.INVALID_CLIENT) .setErrorDescription("無效的客戶端ID") .buildJSONMessage(); return new ResponseEntity(oAuthResponse.getBody(), HttpStatus.valueOf(oAuthResponse.getResponseStatus())); } // 2.檢查客戶端安全key是否正確 if (!lightUserResult.getClientSecret().equals(tokenRequest.getClientSecret())) { OAuthResponse oAuthResponse = OAuthResponse .errorResponse(HttpServletResponse.SC_UNAUTHORIZED) .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT) .setErrorDescription("客戶端安全key認證不通過") .buildJSONMessage(); return new ResponseEntity<>(oAuthResponse.getBody(), HttpStatus.valueOf(oAuthResponse.getResponseStatus())); } // 3.檢查授權碼是否正確 String authCode = tokenRequest.getParam(OAuth.OAUTH_CODE); // 檢查驗證類型,此處只檢查AUTHORIZATION_CODE類型,其他的還有password或REFRESH_TOKEN if (!tokenRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) { if (null == RedisUtil.getRedis().get(authCode)) { OAuthResponse response = OAuthASResponse .errorResponse(HttpServletResponse.SC_BAD_REQUEST) .setError(OAuthError.TokenResponse.INVALID_GRANT) .setErrorDescription("授權碼錯誤") .buildJSONMessage(); return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus())); } } // 4.生成訪問令牌Access Token OAuthIssuer oAuthIssuer = new OAuthIssuerImpl(new MD5Generator()); final String accessToken = oAuthIssuer.accessToken(); // 將訪問令牌加入緩存:accessToken-username RedisUtil.getRedis().set(accessToken, lightUserResult.getUserName()); // 5.生成OAuth響應 OAuthResponse response = OAuthASResponse .tokenResponse(HttpServletResponse.SC_OK) .setAccessToken(accessToken) .setExpiresIn(expiresIn) .buildJSONMessage(); return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus())); } catch (Exception e) { e.printStackTrace(); return new ResponseEntity("內部錯誤", HttpStatus.valueOf(HttpServletResponse.SC_INTERNAL_SERVER_ERROR)); } }
六、簡單測試
@RequestMapping("authority")
@ResponseBody
public JSONObject authority() throws OAuthSystemException, OAuthProblemException {
JSONObject result = new JSONObject();
OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
OAuthClientRequest codeTokenRequest = OAuthClientRequest
.authorizationLocation("http://127.0.0.1:8080/auth-web/oauth/authorize")
.setResponseType(ResponseType.CODE.toString())
.setClientId("c1ebe466-1cdc-4bd3-ab69-77c3561b9dee")
.buildQueryMessage();
//獲取 code
OAuthResourceResponse codeResponse = oAuthClient.resource(
codeTokenRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);
if(codeResponse.getResponseCode() != HttpServletResponse.SC_OK) {
result.put("code", codeResponse.getResponseCode());
result.put("msg", codeResponse.getBody());
} else {
String authCode = codeResponse.getBody();
OAuthClientRequest accessTokenRequest = OAuthClientRequest
.tokenLocation("http://127.0.0.1:8080/auth-web/oauth/accessToken")
.setGrantType(GrantType.AUTHORIZATION_CODE)
.setClientId("c1ebe466-1cdc-4bd3-ab69-77c3561b9dee").setClientSecret("d8346ea2-6017-43ed-ad68-19c0f971738b")
.setCode(authCode).setRedirectURI("http://127.0.0.1:8080/auth-web/")
.buildQueryMessage();
//獲取access token
OAuthAccessTokenResponse tokenResponse =
oAuthClient.accessToken(accessTokenRequest, OAuth.HttpMethod.POST);
if(tokenResponse.getResponseCode() != HttpServletResponse.SC_OK) {
result.put("code", tokenResponse.getResponseCode());
result.put("msg", tokenResponse.getBody());
return result;
} else {
//驗證token
OAuthClientRequest validateRequest =
new OAuthBearerClientRequest("http://127.0.0.1:8080/auth-web/oauth/validate")
.setAccessToken(tokenResponse.getAccessToken()).buildQueryMessage();
OAuthResourceResponse validateResponse = oAuthClient.resource(
validateRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);
if(validateResponse.getResponseCode() != HttpServletResponse.SC_OK) {
result.put("code", validateResponse.getResponseCode());
result.put("msg", validateResponse.getBody());
} else {
JSONObject body = JSON.parseObject(validateResponse.getBody());
result.put("code", body.getString("code"));
result.put("msg", body.getString("msg"));
}
}
}
return result;
}
public static ResponseEntity oauthValidate(HttpServletRequest request) throws OAuthProblemException, OAuthSystemException { // 構建OAuth資源請求 OAuthAccessResourceRequest resourceRequest = new OAuthAccessResourceRequest(request, ParameterStyle.QUERY); // 獲取訪問令牌access Token String accessToken = resourceRequest.getAccessToken(); // 驗證訪問令牌 if (null == RedisUtil.getRedis().get(accessToken)) { // 如果不存在或過期了,返回未驗證錯誤,需重新驗證 OAuthResponse response = OAuthRSResponse .errorResponse(HttpServletResponse.SC_UNAUTHORIZED) .setError(OAuthError.ResourceResponse.INVALID_TOKEN) .setErrorDescription("訪問令牌不存在或已過期,請重新驗證") .buildJSONMessage(); return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus())); } return new ResponseEntity("驗證成功", HttpStatus.valueOf(HttpServletResponse.SC_OK)); }
