简介
- 使用Apache Oltu来做对OAuth的实现,因为它比较轻量、简单、灵活。
- 按照OAuth的角色分布,可以写三个程序:资源服务器、授权服务器和客户端Client各一个,也可以全部写到一个程序里面。
- 本例采用写成两个程序,一个服务器端,一个客户端。
数据库
- 数据库:shiro-oauth2.所在会话:mysql。
- oauth2_user:用户表,资源拥有者。admin/123456。
- oauth2_client:客户端表,存储客户端id和客户端密钥,在进行授权时使用。
服务器端
oltu源码
- 源码分包:
![](http://zhuzhuzai.oss-cn-shenzhen.aliyuncs.com/041748590918173.png)
- issuer:生成授权码和访问令牌,刷新令牌。
- request:封装授权码请求和令牌请求的逻辑,并提供相应的校验服务。
- response:封装授权流程中的响应逻辑,提供生成不同响应结果的方法。
- validator:为request提供校验服务。
issuer
- 主要是俩接口。
- OAuthIssuer接口:默认实现类OAuthIssuerImpl。
public interface OAuthIssuer {
public String accessToken() throws OAuthSystemException;
public String authorizationCode() throws OAuthSystemException;
public String refreshToken() throws OAuthSystemException;
}
- ValueGenerator接口:默认实现类MD5Generator和UUIDValueGenerator,用于生成code和token的字符串。
public interface ValueGenerator {
public String generateValue() throws OAuthSystemException;
public String generateValue(String param) throws OAuthSystemException;
}
request
- 封装请求主要是为了提取其中的参数来验证是否合格,比如client_id是否正确。
- 主要是一个父类,其他都是其子类。
- OAuthRequest:父类,提供最基础的逻辑和方法。
- OAuthAuthzRequest:授权码请求。
- AbstractOAuthTokenRequest:抽象类,为下一个类做准备。
- OAuthTokenRequest:令牌请求。
- OAuthUnauthenticatedTokenRequest:刷新令牌的请求。
- validator部分代码分析
- 封装请求示例:
OAuthTokenRequest oAuthTokenRequest=new OAuthTokenRequest(request);
//比如规定的参数必须要有,至少GrantType、clientId、clientSecret、code、redirectUrl;
//否则抛出异常:OAuthProblemException;另有OAuthSystemException,这个较少出现
validator
- 包下all类实现自validators包的OAuthvalidator接口,其包下还有实现了all方法的AbstractValidator类。
- 本包下类:
![](http://zhuzhuzai.oss-cn-shenzhen.aliyuncs.com/041748590918173.png)
response
- OAuthResponse:构造响应数据的父类,构造方法protected,不能创建实例,实际用到的是其静态内部类OAuthResponseBuilder,是后面要介绍到的两个Builder类的父类;不知道在不在本包;成员如下:
![](http://zhuzhuzai.oss-cn-shenzhen.aliyuncs.com/041749056842324.png)
其中有两个Builder:OAuthResponseBuilder和OAuthErrorResponseBuilder,后者是前者的子类。
OAuthASResponse:子类,提供了组装不同请求的方法,成员如下:
![](http://zhuzhuzai.oss-cn-shenzhen.aliyuncs.com/041749045594768.png)
构造方法是protected,因此不能new此类的实例,我们实际需要用的只是它的两个静态内部类OAuthAuthorizationResponseBuilder和OAuthTokenResponseBuilder,通过它们的方法来生成最终的响应数据。
构造响应的方法基本也就上面框出来3个了,注意到Builder内的all方法都是返回本类的实例,也就是可以无限链式调用。我们先看一个实际使用中的场景:
// 处理授权码请求返回的响应
OAuthResponse oAuthResponse= OAuthASResponse.authorizationResponse(request, 200)
.location(redirectUrl)
.setCode(oauthCode)
.setScope(state)
.buildQueryMessage();
String url=oAuthResponse.getLocationUri();
response.sendRedirect(url);
// 令牌
OAuthResponse authASResponse = OAuthASResponse.tokenResponse(200)
.setAccessToken(access_token)
.setExpiresIn("7200")
.setRefreshToken(refreshToken)
.setTokenType(TokenType.BEARER.toString())
.setParam("re_expires_in", "14400")
.buildJSONMessage();
String json=authASResponse.getBody();
// 错误响应
OAuthResponse authASResponse = OAuthASResponse
.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
.setError(OAuthError.ResourceResponse.INVALID_TOKEN)
.setErrorDescription("invald expired")
.buildJSONMessage();
return new ResponseEntity<String>(authASResponse.getBody(), headers,
HttpStatus.UNAUTHORIZED);
- buildQueryMessage():发起新请求,url即location()中的参数redirectUrl,响应体自然也送至该url。
- buildJSONMessage():返回原页面,响应体被解析为json。
- setter实际就是设置响应参数,最后调用一个buildXxxMessage方法生成一个包含所有响应参数的OAuthResponse对象(注意是OAuthResponse这个父类,而不是OAuthASResponse子类,所以最终都是要用OAuthResponse对象来接收)。
- 对象返回后就可以调用getBody(),getHeaders()之类的方法获取到其中的响应数据。
对于错误响应中的error,在set时可以直接调用alth提供的OAuthError类的常量,它们是不同场景下通用的错误标识。