最近作oauth2預研,查了至關多的資料java
由於現有的項目是使用java 語言來實現的,且不打算直接去實現這一整套的標準。所以先去官網(https://oauth.net/code/)看了下現有的java版實現。其實還有其餘的實現沒有收錄進去。git
比較以後發現資料相對較多的是Apache oltu以及 spring sercurity oauth.由於都是開源的,就去把源代碼都clone下來了。我的認爲Oltu相對來講更輕量,也更簡單,是對oauth2的簡單實現。不少後續校驗的事情都須要咱們本身去作,但這也是它靈活的一面。因此一開始,是決定使用Apache 的oltu。參考了楊開濤的博客(OAuth2集成——《跟我學Shiro》)使用oltu實現了一個簡單的認證服務器。 github
一開始是打算寫三個服務的oauthservice,oauthclient,oauthresource,後來爲了省時間直接把客戶端也集成到服務裏面了,交互界面也只是幾個簡單的輸入框。spring
認證服務主要有幾個接口後端
/login:登陸接口springboot
/Oauth/authorize:獲取受權碼的接口服務器
/Oauth/getCode:這個其實就是受權碼接口,只是例子中後端沒有存儲登陸狀態,作了箇中轉app
/Oauth/ accesstoken:獲取訪問令牌ui
下面來講說每一個接口具體作了什麼事url
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; }
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