前言

前面的理解OAuth2.0认证与客户端授权码模式详解,我们大致了解了Oauth2.0授权模式四种的授权码模式,清楚了授权码模式的大致流程。这里简单的模拟一下基于授权码模式的客户端和服务端代码实现(这里服务端包含的验证服务端和资源服务端,都是在同一个应用中)。

回顾大致授权流程

这里写图片描述

  • 图中步骤1,请求授权,例如我们要登录csdn,这里我们想用qq账号登录,这时候就需要腾讯开放平台进行授权,具体操作如下图
    这里写图片描述
    这里简单模拟,在客户端web项目中构造一个oauth的客户端请求对象(OAuthClientRequest),在此对象中携带客户端信息(clientId、accessTokenUrl、response_type、redirectUrl),将此信息放入http请求中,重定向到服务端。
  • 上图步骤2,在服务端web项目中接受第一步传过来的request,从中获取客户端信息,可以自行验证信息的可靠性。同时构造一个oauth的code授权许可对象(OAuthAuthorizationResponseBuilder),并在其中设置授权码code,将此对象传回客户端。
  • 上图步骤3,在在客户端web项目中接受第二步的请求request,从中获得code。同时构造一个oauth的客户端请求对象(OAuthClientRequest),此次在此对象中不仅要携带客户端信息(clientId、accessTokenUrl、clientSecret、GrantType、redirectUrl),还要携带接受到的code。再构造一个客户端请求工具对象(oAuthClient),这个工具封装了httpclient,用此对象将这些信息以post的方式请求到服务端,目的是为了让服务端返回资源访问令牌。
  • 上图步骤4,在服务端web项目中接受第三步传过来的request,从中获取客户端信息和code,并自行验证。再按照自己项目的要求生成访问令牌(accesstoken),同时构造一个oauth响应对象(OAuthASResponse),携带生成的访问指令(accesstoken),返回给第三步中客户端的oAuthClient。oAuthClient接受响应之后获取accesstoken。
  • 上图步骤5,此时客户端web项目中已经有了从服务端返回过来的accesstoken,那么在客户端构造一个服务端资源请求对象(OAuthBearerClientRequest),在此对象中设置服务端资源请求URI,并携带上accesstoken。再构造一个客户端请求工具对象(oAuthClient),用此对象去服务端靠accesstoken换取资源。
  • 上图步骤6,在服务端web项目中接受第五步传过来的request,从中获取accesstoken并自行验证。之后就可以将客户端请求的资源返回给客户端了。

客户端代码

/**
* @ClassName: ClientController 
* @Description: Oauth客户端 
* @author qinhaihai 
* @date 2018年5月24日
*
 */
@Controller
@RequestMapping("/clientController")
public class ClientController{
    String clientId = null;
    String clientSecret = null;
    String accessTokenUrl = null;
    String userInfoUrl = null;
    String redirectUrl = null;
    String response_type = null;
    String code= null;

    //提交申请code的请求,对应上图中的步骤一
    @RequestMapping("/requestServerCode")
    public String requestServerCode(HttpServletRequest request, HttpServletResponse response) 
            throws OAuthProblemException{
        clientId = "clientId";
        accessTokenUrl = "responseCode";
        redirectUrl = "http://localhost:8081/oauthclient01/clientController/callbackCode";
        response_type = "code";
        String requestUrl = null;
        try {
            //构建oauth的请求。设置授权服务地址(accessTokenUrl)、clientId、response_type、redirectUrl
            OAuthClientRequest accessTokenRequest = OAuthClientRequest
            .authorizationLocation(accessTokenUrl)
            .setResponseType(response_type)
            .setClientId(clientId)
            .setRedirectURI(redirectUrl)
            .buildQueryMessage();
            requestUrl = accessTokenRequest.getLocationUri();
            System.out.println("获取授权码方法中的requestUrl的值----"+requestUrl);
        } catch (OAuthSystemException e) {
            e.printStackTrace();
        }
        return "redirect:http://localhost:8082/oauthserver/"+requestUrl ;
    }

    //接受客户端返回的code,提交申请access token的请求,对应上图中的步骤三
    @RequestMapping("/callbackCode")
    public Object toLogin(HttpServletRequest request) throws OAuthProblemException{
        clientId = "clientId";
        clientSecret = "clientSecret";
        accessTokenUrl="http://localhost:8082/oauthserver/responseAccessToken";
        userInfoUrl = "userInfoUrl";
        redirectUrl = "http://localhost:8081/oauthclient01/clientController/accessToken";
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
        try {
            OAuthClientRequest accessTokenRequest = OAuthClientRequest
                .tokenLocation(accessTokenUrl)
                    .setGrantType(GrantType.AUTHORIZATION_CODE)
                    .setClientId(clientId)
                    .setClientSecret(clientSecret)
                    .setCode(httpRequest.getParameter("code"))
                    .setRedirectURI(redirectUrl)
                    .buildQueryMessage();
            //去服务端请求access token,并返回响应
            OAuthAccessTokenResponse oAuthResponse = oAuthClient.accessToken(accessTokenRequest, OAuth.HttpMethod.POST);
            //获取服务端返回过来的access token 
            String accessToken = oAuthResponse.getAccessToken();
            //查看access token是否过期
            //Long expiresIn = oAuthResponse.getExpiresIn();
            return "redirect:http://localhost:8081/oauthclient01/clientController/accessToken?accessToken="+accessToken;
        } catch (OAuthSystemException e) {
            e.printStackTrace();
        }
        return null;
    }

    //接受服务端传回来的access token,由此token去请求服务端的资源(用户信息等),对应上图中的步骤五
    @RequestMapping("/accessToken")
    public ModelAndView accessToken(String accessToken) {
        userInfoUrl = "http://localhost:8082/oauthserver/userInfo";
        OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
        try {
            OAuthClientRequest userInfoRequest = new OAuthBearerClientRequest(userInfoUrl)
            .setAccessToken(accessToken).buildQueryMessage();
            OAuthResourceResponse resourceResponse = oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);
            String username = resourceResponse.getBody();
            ModelAndView modelAndView = new ModelAndView("usernamePage");
            modelAndView.addObject("username", username);
            return modelAndView;
        } catch (OAuthSystemException e) {
            e.printStackTrace();
        } catch (OAuthProblemException e) {
            e.printStackTrace();
        }
        return null;
    }
}

服务端代码

/**
* @ClassName: AuthorizeController 
* @Description: 服务端授权Controller 
* @author aiqinhai 
* @date 2018年5月24日 下午10:00:01 
*/
@Controller
public class AuthorizeController{
    //向客户端返回授权许可码 code,对应上图中的步骤二
    @RequestMapping("/responseCode")
    public Object responseCode(Model model,HttpServletRequest request){
      try {
      //构建OAuth 授权请求  
      OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request); 
      System.out.println("授权服务器获取的clientID----"+oauthRequest.getClientId());
      System.out.println("返回类型----"+oauthRequest.getResponseType());
      System.out.println("重定向地址---"+oauthRequest.getRedirectURI());
      if(oauthRequest.getClientId()!=null&&oauthRequest.getClientId()!=""){
        //设置授权码  
        String authorizationCode = "authorizationCode";
        //进行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();
        String responceUri =response.getLocationUri();
        System.out.println("redirectURI是----"+redirectURI);
        System.out.println("responceUri是----"+responceUri);
        //根据OAuthResponse返回ResponseEntity响应
        HttpHeaders headers = new HttpHeaders();
        try {
            headers.setLocation(new URI(responceUri));
          } catch (URISyntaxException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        return "redirect:"+responceUri;
        }
    } catch (OAuthSystemException e) {
        e.printStackTrace();
    } catch (OAuthProblemException e) {
        e.printStackTrace();
    }
    return null;
    }
}

上面的responseCode,对应了上图的步骤二,返回authorizationCode授权码到客户端。

/**
 * 
* @ClassName: AccessTokenController 
* @Description: 根据授权码生成accessToken
* @author aiqinlhai 
* @date 2018年5月24日
 */
@Controller
public class AccessTokenController {

    //获取客户端的code码,向客户端返回access token
    @RequestMapping(value="/responseAccessToken",method = RequestMethod.POST)  
    public HttpEntity token(HttpServletRequest request){
        OAuthIssuer oauthIssuerImpl=null;
        OAuthResponse response=null;
        //构建OAuth请求  
          try {
            OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);
            String authCode = oauthRequest.getParam(OAuth.OAUTH_CODE);
            System.out.println("客户端传过来的授权码是----"+authCode);
            String clientSecret = oauthRequest.getClientSecret();
            if(clientSecret!=null||clientSecret!=""){
                //生成Access Token
                oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
                final String accessToken = oauthIssuerImpl.accessToken();
                System.out.println(accessToken);
                //生成OAuth响应
                response = OAuthASResponse
                .tokenResponse(HttpServletResponse.SC_OK)
                .setAccessToken(accessToken)
                .buildJSONMessage();
            }
          //根据OAuthResponse生成ResponseEntity
            return new ResponseEntity<String>(response.getBody(), 
                    HttpStatus.valueOf(response.getResponseStatus()));
        } catch (OAuthSystemException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (OAuthProblemException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }
}

上面的代码对应上图的步骤4,验证客户端的授权码,并返回accessToken。

/**
 * 
* @ClassName: UserInfoController 
* @Description: 根据客户端的accessToken来返回用户信息到客户端
* @author aiqinhai 
* @date 2018年5月24
 */
@Controller
public class UserInfoController {
    @RequestMapping("/userInfo") 
    public HttpEntity<String> userInfo(HttpServletRequest request) 
            throws OAuthSystemException{   
        try {
            //获取客户端传来的OAuth资源请求
            OAuthAccessResourceRequest oauthRequest = new 
                    OAuthAccessResourceRequest(request, ParameterStyle.QUERY);
            //获取Access Token  
              String accessToken = oauthRequest.getAccessToken();  
              System.out.println("从客户端获取的accessToken----"+accessToken);
              //验证Access Token  
              if (accessToken==null||accessToken=="") {  
              // 如果不存在/过期了,返回未验证错误,需重新验证  
              OAuthResponse oauthResponse = OAuthRSResponse  
              .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)  
              .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<String>(headers, HttpStatus.UNAUTHORIZED);  
              }
              //这里没有从数据库查询了,简单指定为"aiqinhai"  
              String username="aiqinhai";
              return new ResponseEntity<String>(username, HttpStatus.OK);  
        } catch (OAuthProblemException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          //检查是否设置了错误码  
          String errorCode = e.getError();  
          if (OAuthUtils.isEmpty(errorCode)) {  
            OAuthResponse oauthResponse = OAuthRSResponse  
           .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)  
           .buildHeaderMessage();  
            HttpHeaders headers = new HttpHeaders();  
            headers.add(OAuth.HeaderType.WWW_AUTHENTICATE,   
            oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));  
            return new ResponseEntity<String>(headers, HttpStatus.UNAUTHORIZED);  
          }  
          OAuthResponse oauthResponse = OAuthRSResponse  
           .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)  
           .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<String>(HttpStatus.BAD_REQUEST);  
        }  
    }
}

上段代码对应上图中的步骤6,验证accessToken,返回用户请求资源,这里简单用username来模拟用户请求的资源。
服务端控制台输出如下
这里写图片描述

相关依赖jar包

客户端依赖jar包

<dependency>  
<groupId>org.apache.oltu.oauth2</groupId>  
<artifactId>org.apache.oltu.oauth2.client</artifactId>  
<version>0.31</version>  
</dependency>

服务端依赖jar包

 <dependency>  
      <groupId>org.apache.oltu.oauth2</groupId>  
      <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>  
      <version>0.31</version>  
  </dependency>  
  <dependency>  
      <groupId>org.apache.oltu.oauth2</groupId>  
      <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>  
      <version>0.31</version>  
 </dependency> 
Logo

Authing 是一款以开发者为中心的全场景身份云产品,集成了所有主流身份认证协议,为企业和开发者提供完善安全的用户认证和访问管理服务

更多推荐