简介

  • 使用Apache Oltu来做对OAuth的实现,因为它比较轻量、简单、灵活。
  • 按照OAuth的角色分布,可以写三个程序:资源服务器、授权服务器和客户端Client各一个,也可以全部写到一个程序里面。
  • 本例采用写成两个程序,一个服务器端,一个客户端。

数据库

  • 数据库:shiro-oauth2.所在会话:mysql。
  • oauth2_user:用户表,资源拥有者。admin/123456。
  • oauth2_client:客户端表,存储客户端id和客户端密钥,在进行授权时使用。

服务器端

oltu源码
  • 源码分包:
  • 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类。
  • 本包下类:
response
  • OAuthResponse:构造响应数据的父类,构造方法protected,不能创建实例,实际用到的是其静态内部类OAuthResponseBuilder,是后面要介绍到的两个Builder类的父类;不知道在不在本包;成员如下:
  • 其中有两个Builder:OAuthResponseBuilder和OAuthErrorResponseBuilder,后者是前者的子类。

  • OAuthASResponse:子类,提供了组装不同请求的方法,成员如下:

  • 构造方法是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类的常量,它们是不同场景下通用的错误标识。

  • 参考文章

<