由于公司业务需要,现将在springboot中部署OAuth2服务端的做法记录下来。
OAuth2的流程大致如下:
(A)用户打开客户端以后,客户端要求用户给予授权。
(B)用户同意给予客户端授权。
(C)客户端使用上一步获得的授权,向认证服务器申请令牌。
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源。
以流程图的形式展现:
具体可以参考:OAuth 认证流程详解
引入对应的jar包
<dependency>
<groupId>org.apache.oltu.oauth2groupId>
<artifactId>org.apache.oltu.oauth2.authzserverartifactId>
<version>0.31version>
dependency>
<dependency>
<groupId>org.apache.oltu.oauth2groupId> <artifactId>org.apache.oltu.oauth2.resourceserverartifactId>
<version>0.31version>
dependency>
授权获取code
try {
//构建OAuth 授权请求 这里有对应的参数验证
OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
User user = userService.findByAppId(oauthRequest.getClientId());
// 检查appId是否正确
if (null == user) {
OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
.setError(OAuthError.TokenResponse.INVALID_CLIENT)
.setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION)
.buildJSONMessage();
return new ResponseEntity<>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
}
// 输出请求地址
String url = String.valueOf(request.getRequestURL());
String uri = String.valueOf(request.getRequestURI());
if (StringUtil.isNotEmpty(uri)) {
url = url.substring(0, url.length() - uri.length());
}
if (!user.getUrl().equalsIgnoreCase(url)) {
OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
.setError(OAuthError.TokenResponse.INVALID_CLIENT)
.setErrorDescription(Constants.INVALID_URL_ADDRESS)
.buildJSONMessage();
return new ResponseEntity<>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
}
//生成授权码
String authorizationCode;
//responseType目前仅支持CODE,另外还有TOKEN
String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
if (responseType.equals(ResponseType.CODE.toString())) {
OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
authorizationCode = oauthIssuerImpl.authorizationCode();
oAuthService.addAuthCode(authorizationCode, "aLOCK_DXY");
} else {
OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
.setError(OAuthError.TokenResponse.INVALID_CLIENT)
.setErrorDescription(Constants.INVALID_RESPONSE_TYPE)
.buildJSONMessage();
return new ResponseEntity<>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
}
//进行OAuth响应构建
OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);
//设置授权码
builder.setCode(authorizationCode);
//得到到客户端重定向地址
String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);
//构建响应
final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();
//根据OAuthResponse返回ResponseEntity响应
HttpHeaders headers = new HttpHeaders();
headers.setLocation(new URI(response.getLocationUri()));
return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
} catch (OAuthProblemException e) {
//出错处理
String redirectUri = e.getRedirectUri();
if (OAuthUtils.isEmpty(redirectUri)) {
//告诉客户端没有传入redirectUri直接报错
return new ResponseEntity<>("OAuth callback url needs to be provided by client!!!", HttpStatus.NOT_FOUND);
}
//返回错误消息(如?error=)
final OAuthResponse response = OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND).error(e).location(redirectUri).buildQueryMessage();
HttpHeaders headers = new HttpHeaders();
headers.setLocation(new URI(response.getLocationUri()));
return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
}
根据code获取accessToken
try {
//构建OAuth请求
OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);
//检查提交的客户端id是否正确
if (null == userService.findByAppId(oauthRequest.getParam("client_id"))) {
OAuthResponse response =
OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
.setError(OAuthError.TokenResponse.INVALID_CLIENT)
.setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION)
.buildJSONMessage();
return new ResponseEntity<>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
}
// 检查客户端安全KEY是否正确
if (null == userService.findByAppSecret(oauthRequest.getParam("client_secret"))) {
OAuthResponse response =
OAuthASResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
.setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
.setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION)
.buildJSONMessage();
return new ResponseEntity<>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
}
String authCode = oauthRequest.getParam(OAuth.OAUTH_CODE);
// 检查验证类型,此处只检查AUTHORIZATION_CODE类型,其他的还有PASSWORD或REFRESH_TOKEN
if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) {
if (!oAuthService.checkAuthCode(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()));
}
}
//生成Access Token
OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
final String accessToken = oauthIssuerImpl.accessToken();
final String refreshToken = oauthIssuerImpl.refreshToken();
// oAuthService.addAccessToken(accessToken, oAuthService.getUsernameByAuthCode(authCode));
//生成OAuth响应 2小时过期
OAuthResponse response = OAuthASResponse
.tokenResponse(HttpServletResponse.SC_OK)
.setAccessToken(accessToken)
.setRefreshToken(refreshToken)
.setExpiresIn(String.valueOf(oAuthService.getExpireIn()))
.buildJSONMessage();
//根据OAuthResponse生成ResponseEntity
return new ResponseEntity<>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
} catch (OAuthProblemException e) {
//构建错误响应
OAuthResponse res = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e).buildJSONMessage();
return new ResponseEntity<>(res.getBody(), HttpStatus.valueOf(res.getResponseStatus()));
}
根据accessToken获取用户信息
try {
//构建OAuth资源请求
OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(request, ParameterStyle.QUERY);
//获取Access Token
String accessToken = oauthRequest.getAccessToken();
//验证Access Token
if (!oAuthService.checkAccessToken(accessToken)) {
// 如果不存在/过期了,返回未验证错误,需重新验证
OAuthResponse oauthResponse = OAuthRSResponse
.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
.setRealm(Constants.RESOURCE_SERVER_NAME)
.setError(OAuthError.ResourceResponse.INVALID_TOKEN)
.buildHeaderMessage();
HttpHeaders headers = new HttpHeaders();
headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
return new ResponseEntity<>(headers, HttpStatus.UNAUTHORIZED);
}
//返回用户名
String username = "aLOCK2";
return new ResponseEntity<>(username, HttpStatus.OK);
} catch (OAuthProblemException e) {
//检查是否设置了错误码
String errorCode = e.getError();
if (OAuthUtils.isEmpty(errorCode)) {
OAuthResponse oauthResponse = OAuthRSResponse
.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
.setRealm(Constants.RESOURCE_SERVER_NAME)
.buildHeaderMessage();
HttpHeaders headers = new HttpHeaders();
headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
return new ResponseEntity<>(headers, HttpStatus.UNAUTHORIZED);
}
OAuthResponse oauthResponse = OAuthRSResponse
.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
.setRealm(Constants.RESOURCE_SERVER_NAME)
.setError(e.getError())
.setErrorDescription(e.getDescription())
.setErrorUri(e.getUri())
.buildHeaderMessage();
HttpHeaders headers = new HttpHeaders();
headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
这里面没有加入刷新机制,感兴趣的可以自己加下。这样一个大致的OAuth2流程就搭起来来了。