一、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)); }
七、项目地址
时间: 2024-11-05 19:12:56