oauth2 学习(一)-使用Apache oltu实现oauth2的授权服务器

时间:2022-12-19 21:00:59

         最近做oauth2预研,查了相当多的资料

         因为现有的项目是使用java 语言来实现的,且不打算直接去实现这一整套的标准。因此先去官网(https://oauth.net/code/)看了下现有的java版实现。其实还有其他的实现没有收录进去。

 oauth2 学习(一)-使用Apache oltu实现oauth2的授权服务器

  比较之后发现资料相对较多的是Apache oltu以及 spring sercurity oauth.因为都是开源的,就去把源代码都clone下来了。个人认为Oltu相对来说更轻量,也更简单,是对oauth2的简单实现。很多后续校验的事情都需要我们自己去做,但这也是它灵活的一面。所以一开始,是决定使用Apache 的oltu。参考了杨开涛的博客(OAuth2集成——《跟我学Shiro》)使用oltu实现了一个简单的认证服务器。        

 

         一开始是打算写三个服务的oauthservice,oauthclient,oauthresource,后来为了省时间直接把客户端也集成到服务里面了,交互界面也只是几个简单的输入框。

         认证服务主要有几个接口

 

         /login:登录接口

  /Oauth/authorize:获取授权码的接口

  /Oauth/getCode:这个其实就是授权码接口,只是例子中后端没有存储登录状态,做了个中转

  /Oauth/ accesstoken:获取访问令牌

下面来说说每个接口具体做了什么事

  1. 获取授权码
    1. 将请求转换成oltu的认证请求OauthAuthzRequest
    2. 从 OauthAuthzRequest 读取客户端信息(clientId,redirectUrl,response_type)
    3. 校验客户端信息
    4. 校验成功后生成访问令牌
    5. 存储访问令牌
    6. 使用oltu的OAuthASResponse构建oauth响应
    7. 在响应中设置好授权码,state等信息
    8. 重定向到客户端的redirectUrl
      public Object getCode(HttpServletRequest request) {
      try {
      OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
      OAuthResponse oAuthResponse;
      String clientId=oauthRequest.getClientId();
      //校验client信息
      if(!oauthClientService.checkClient(clientId))
      {
      return ControllerHelper
      .getResponseEntity(HttpServletResponse.SC_BAD_REQUEST
      , OAuthError.TokenResponse.INVALID_CLIENT
      , ErrorConstants.ERROR_CLIENT_MSG);
      }
      //获取登陆信息
      //已经登录校验内部token信息,没有登陆,校验登陆信息
      String token=request.getParameter("token");
      if(StringUtils.isEmpty(token))//token不存在及用户没有登陆,非法访问
      {
      return ControllerHelper
      .getResponseEntity(HttpServletResponse.SC_BAD_REQUEST
      , OAuthError.CodeResponse.ACCESS_DENIED
      , ErrorConstants.ERROR_CLIENT_LOGIN);
      }
      else {//校验token 服务器端对应的token是否存在,及获取用户信息等
      // checktoken()
      }
      //生成授权码
      String authcode=null;
      String responseType=oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
      if(responseType.equals(ResponseType.CODE.toString()))
      {
      OAuthIssuerImpl oAuthIssuerImpl=new OAuthIssuerImpl(new MD5Generator());
      authcode=oAuthIssuerImpl.authorizationCode();
      //保存授权码
      oauthClientService.saveCode(clientId, authcode);
      }
      //Oauth 响应
      OAuthASResponse.OAuthAuthorizationResponseBuilder builder=
      OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);
      //设置授权码
      builder.setCode(authcode);
      String redirectURI=oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);

      oAuthResponse=builder.location(redirectURI).buildQueryMessage();
      //根据OAuthResponse返回ResponseEntity响应
      HttpHeaders headers = new HttpHeaders();
      headers.setLocation(new URI(oAuthResponse.getLocationUri()));
      return new ResponseEntity(headers, HttpStatus.valueOf(oAuthResponse.getResponseStatus()));


      } catch (Exception e) {
      logger.error(e.getCause().getMessage(),e);
      }
      return null;
      }

        


  2. 获取访问令牌
  1. 将请求转换成oltu的token 获取请求OAuthTokenRequest
  2. 从OauthTokenRequest读取客户端信息
  3. 校验客户端信息
  4. 生成访问令牌token
  5. 存储访问令牌
  6. 构建oauth2响应oAuthResponse
  7. 返回到客户端
public Object getToken(HttpServletRequest request) throws OAuthSystemException {
try {
OAuthTokenRequest oAuthTokenRequest= new OAuthTokenRequest(request);
String clientId=oAuthTokenRequest.getClientId();
String clientKey= oAuthTokenRequest.getClientSecret();

if(!oauthClientService.checkClient(clientId,clientKey))
{
return ControllerHelper.getResponseEntity(HttpServletResponse.SC_BAD_REQUEST, OAuthError.TokenResponse.INVALID_CLIENT, ErrorConstants.ERROR_CLIENT_MSG);
}
OAuthResponse oAuthResponse;

String authcode= oAuthTokenRequest.getCode();
String grantType= oAuthTokenRequest.getGrantType();
if(GrantType.AUTHORIZATION_CODE.toString().equals(grantType) && authcode.equals(oauthClientService.getCode(clientId)))
{
//生成token
OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
final String accessToken = oauthIssuerImpl.accessToken();
oauthClientService.saveAccessToken(accessToken, "");


//生成OAuth响应
oAuthResponse = OAuthASResponse
.tokenResponse(HttpServletResponse.SC_OK)
.setAccessToken(accessToken)
.setExpiresIn(String.valueOf( 3600L))
.buildJSONMessage();

//根据OAuthResponse生成ResponseEntity
return new ResponseEntity(oAuthResponse.getBody(), HttpStatus.valueOf(oAuthResponse.getResponseStatus()));

}
else{
return ControllerHelper.getResponseEntity(HttpServletResponse.SC_BAD_REQUEST, OAuthError.TokenResponse.INVALID_GRANT, ErrorConstants.ERROR_AUTH_CODE);
}


} catch (Exception e) {
logger.error(e.getMessage(),e);
return ControllerHelper.getResponseEntity(HttpServletResponse.SC_BAD_REQUEST, ErrorConstants.ERROR_UNKNOW, e.getCause().getMessage());
}
}

  

客户端主要有一下两个接口

/requestAuth: 重定向到拼接好的授权请求url

 

	@RequestMapping("/requestAuth")
public ModelAndView requestAuth(@ModelAttribute("oauthParams") OauthParam oauthParams) {
try {
OAuthClientRequest request = OAuthClientRequest
.authorizationLocation(oauthParams.getAuthzEndpoint())
.setClientId(oauthParams.getClientId())
.setRedirectURI(oauthParams.getRedirectUri())
.setResponseType(ResponseType.CODE.toString())
.setScope(oauthParams.getScope())
.setState(oauthParams.getState())
.buildQueryMessage();

return new ModelAndView(new RedirectView(request.getLocationUri()));
} catch (Exception e) {
logger.error(e.getMessage(),e);
return null;
}
}

  

/redirect:获取授权码后,处理授权码的重定向地址。

 

  OAuthAuthzResponse oar = null;
oar = OAuthAuthzResponse.oauthCodeAuthzResponse(request);
String code = oar.getCode();//获取授权码
OAuthClientRequest request2 =OAuthClientRequest
.tokenLocation(oauthParams.getTokenEndpoint())
.setClientId(oauthParams.getClientId())
.setClientSecret(oauthParams.getClientSecret())
.setRedirectURI(oauthParams.getRedirectUri())
.setCode(code)
.setGrantType(GrantType.AUTHORIZATION_CODE)
.buildBodyMessage();

OAuthClient client=new OAuthClient(new URLConnectionClient());

Class<? extends OAuthAccessTokenResponse> cl = OAuthJSONAccessTokenResponse.class;
//请求token
OAuthAccessTokenResponse oauthResponse=client.accessToken(request2,cl);
String token=oauthResponse.getAccessToken();//获取token

 源码地址:https://github.com/huanglin101/springboot_oltu_oauth2.git