OAuth2.0原理与简单实现演示

最近在学习Oauth,趁有时间把一些实践经验和理解记录下来,目前oauth最新的版本是2.0,相比1.0更简单更安全,在企业的业务系统中可用来实现SSO

 

1OAuth的简述

OAuthOpen Authorization,开放授权)是为用户资源的授权定义了一个安全、开放及简单的标准,第三方无需知道用户的账号及密码,就可获取到用户的授权信息,并且这是安全的。

 

2、名称定义

在详细讲解OAuth 2.0之前,需要了解几个专用名词。它们对读懂后面的讲解,至关重要。

1Third-party application:第三方应用程序,本文中又称"客户端"client)。

2Resource Owner:资源所有者,本文中又称"用户"user)。

3User Agent:用户代理,本文中就是指浏览器。

4Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。

5Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。

 

知道了上面这些名词,就不难理解,OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务商提供商"进行互动。

 

3OAuth的原理



 

上图是Oauth2.0其中的授权码模式(authorization code),因为这个模式比较常用,我主要学习这个为主,图中我分为5个步骤讲解

 

【A】    用户请求访问客户端,或者将前者导向至认证服务器,带上参数:client_idredirect_url

【B】    用户登录认证服务器,并选择是否授权给客户端访问

【C】    用户选择同意授权,认证服务器把用户导向至redirect_url,并且附上授权码(authorization code

【D】   客户端向认证服务器请求access token,需要带上参数:client_idclient_secretredirect_uri,和上一步获取的authorization code;认证服务器检查参数正确则返回access tonken表示授权成功

【E】    客户端使用access token请求资源服务器获取用户资源。

 

3、OAuth2.0简单实现(java

       国外有几个开源的java版的oauth实现,可以帮助较快的搭建一个oauth服务器,例如以下几个(可点击打开对应官网)

·         Java

·         Apache Oltu

·         Spring Security for OAuth

·         Apis Authorization Server (v2-31)

·         Restlet Framework (draft 30)

·         Apache CXF

 

 

Apache Oltu

目前我只是尝试了Oltu,比较轻量级,也不需要依赖其他服务,比较简单,但是官方的文档实在简陋,也是花了些时间才大概搞明白怎么用,下面就放出代码给大家演示下

 

服务端

获取授权码服务端代码示例

@RequestMapping("/authorize")

public ModelAndView authorize(HttpServletRequest request)

           throws OAuthSystemException, OAuthProblemException, URISyntaxException {

       ModelAndView mav = new ModelAndView();

       // 构建OAuth请求

       OAuthAuthzRequest oAuthzRequest = new OAuthAuthzRequest(request);

       // 获取OAuth客户端Id

       String clientId = oAuthzRequest.getClientId();

       // 校验客户端Id是否正确

       if (!checkClientId(clientId)) {

           mav.addObject("msg", "无效的客户ID");

           mav.setViewName("forward:/ljdp/oauth/authorizefail");

           returnmav;

       }

      

       //检查用户是否登陆和同意授权,如何还没登陆则跳转至登陆页面,然后进行授权提示

       //待开发。。。

      

      

       //生成授权码

       String authCode = null;

       String responseType = oAuthzRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);

       //ResponseType仅支持CODETOKEN

       if(responseType.equals(ResponseType.CODE.toString())){

           OAuthIssuerImpl oAuthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());

           authCode = oAuthIssuerImpl.authorizationCode();

           System.out.println("授权码="+authCode);

       }

      

       //获取客户端重定向地址

       String redirectURI = oAuthzRequest.getParam(OAuth.OAUTH_REDIRECT_URI);

       mav.setViewName("redirect:"+redirectURI+"?code="+authCode);

       returnmav;

}

 

 

获取Access Token服务端代码示例

@RequestMapping("/accesstoken")

public ResponseEntity<String> accessToken(HttpServletRequest request) throws OAuthSystemException, OAuthProblemException{

       //构建OAuth请求

       OAuthTokenRequest tokenRequest = new OAuthTokenRequest(request);

       //获取OAuth客户端Id

       String clientId = tokenRequest.getClientId();

       //校验客户端Id是否正确

       if(!checkClientId(clientId)){

           OAuthResponse oAuthResponse = OAuthASResponse

                  .errorResponse(HttpServletResponse.SC_BAD_REQUEST)

                  .setError(OAuthError.TokenResponse.INVALID_CLIENT)

                  .setErrorDescription("无效的客户端Id")

                  .buildJSONMessage();

           System.out.println(oAuthResponse.getBody());

           return new ResponseEntity<String>(oAuthResponse.getBody(), HttpStatus.valueOf(oAuthResponse.getResponseStatus()));

       }

      

       //检查客户端安全KEY是否正确

       if(!checkClientSecret(tokenRequest.getClientSecret())){

           OAuthResponse response = OAuthResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)

                     .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)

                     .setErrorDescription("客户端安全KEY认证不通过")

                      .buildJSONMessage();

           return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));

       }

      

       //检查redirect_uri是否和认证的一致

       if(!checkRedirectUri(tokenRequest.getRedirectURI())){

           OAuthResponse response = OAuthResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)

                  .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)

                  .setErrorDescription("客户端认证不通过")

                  .buildJSONMessage();

           return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));

       }

      

       //验证类型,有AUTHORIZATION_CODE/PASSWORD/REFRESH_TOKEN/CLIENT_CREDENTIALS

        if(tokenRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())){

           String authCode = tokenRequest.getParam(OAuth.OAUTH_CODE);

           if(!checkAuthCode(authCode)){

              OAuthResponse response = OAuthResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)

                     .setError(OAuthError.TokenResponse.INVALID_GRANT)

                       .setErrorDescription("错误的授权码") 

                       .buildJSONMessage();

              return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));

           }

           //生成访问令牌

           OAuthIssuerImpl authIssuerImpl = new OAuthIssuerImpl(new MD5Generator());

           String accessToken = authIssuerImpl.accessToken();

          

           //生成OAuth响应

           OAuthResponse response = OAuthASResponse

                  .tokenResponse(HttpServletResponse.SC_OK)

                  .setAccessToken(accessToken)

                  .setExpiresIn("1000")

                  .buildJSONMessage();

           System.out.println(response.getBody());

//         HttpHeaders headers = new HttpHeaders();

//         headers.setContentType(MediaType.APPLICATION_JSON);

           return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));

       }

      

       OAuthResponse response = OAuthResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)

              .setError(OAuthError.TokenResponse.UNSUPPORTED_GRANT_TYPE)

                .setErrorDescription("不支持此授权类型") 

                .buildJSONMessage();

       return new ResponseEntity<String>(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));

}

 

Accesstoken获取授权用户资源服务器端代码示例

@RequestMapping("/resource")

@ResponseBody

public AuthUserInfo resource(HttpServletRequest request, HttpServletResponse response) {

// Make the OAuth Request out of this request and validate it

    // Specify where you expect OAuth access token (request header, body

    // or query string)

    OAuthAccessResourceRequest oauthRequest =

new OAuthAccessResourceRequest(request, ParameterStyle.QUERY);

 

    // Get the access token

    String accessToken = oauthRequest.getAccessToken();

   

    // ... validate access token

 

    //验证通过返回用户信息

    return new AuthUserInfo("test", "测试");

}

 

 

客户

获取accessToken代码示例

OAuthClientRequest request = OAuthClientRequest

       //获取accesstoken地址

       .tokenLocation("http://localhost:8080/ljdp/oauth/accesstoken")

       //授权方式

       .setGrantType(GrantType.AUTHORIZATION_CODE)

       //你的clientidsecret

       .setClientId("your-application-client-id")

       .setClientSecret("your-application-client-secret")

       //第一步重定向的uri

       .setRedirectURI("http://localhost:8080/ljdp/oauth/getcode")

       //第一步获取的认证码

       .setCode("****")

       .buildQueryMessage();

 

//create OAuth client that uses custom http client under the hood

OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());

 

//Custom response classes are an easy way to deal with oauth providers that introduce modifications to

//OAuth 2.0 specification

OAuthJSONAccessTokenResponse oAuthResponse = oAuthClient.accessToken(request, OAuthJSONAccessTokenResponse.class);

 

String accessToken = oAuthResponse.getAccessToken();

Long expiresIn = oAuthResponse.getExpiresIn();

 

accesstoken获取用户资源代码示例

OAuthClientRequest bearerClientRequest = new OAuthBearerClientRequest(

       "http://localhost:8080/ljdp/oauth/resource")

       .setAccessToken("********")

       .buildQueryMessage();

 

OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());

 

OAuthResourceResponse resourceResponse = oAuthClient.resource(

       bearerClientRequest, OAuth.HttpMethod.GET,

       OAuthResourceResponse.class);

 

System.out.println(resourceResponse.getBody());

System.out.println(resourceResponse.getResponseCode());

 

 

 

 

猜你喜欢

转载自hzy0769.iteye.com/blog/2296997