要實現OAuth服務端,就得先理解客戶端的調用流程,服務提供商實現可能也有些區別,實現OAuth服務端的方式不少,具體可能看 http://oauth.net/code/node
各語言的實現有(我使用了Apache Oltu):git
- Java
- NodeJS
- NodeJS OAuth 2.0 Provider
- Mozilla Firefox Accounts. A full stack Identy Provider system developed to support Firefox market place and other services
- Ruby
- .NET
實現主要涉及參數配置以下:
受權碼設置(code)
第三方經過code進行獲取 access_token的時候須要用到,code的超時時間爲10分鐘,一個code只能成功換取一次access_token即失效。
受權做用域(scope)
做用域表明用戶受權給第三方的接口權限,第三方應用須要向服務端申請使用相應scope的權限後,通過用戶受權,獲取到相應access_token後方可對接口進行調用。
令牌有效期(access_token)
access_token是調用受權關係接口的調用憑證,因爲access_token有效期(目前爲2個小時)較短,當access_token超時後,能夠使用refresh_token進行刷新,access_token刷新結果有兩種:
1. 若access_token已超時,那麼進行refresh_token會獲取一個新的access_token,新的超時時間;
2. 若access_token未超時,那麼進行refresh_token不會改變access_token,但超時時間會刷新,至關於續期access_token。
refresh_token擁有較長的有效期(30天),當refresh_token失效的後,須要用戶從新受權。github
項目結構以下:
AuthzController:獲取受權碼
TokenController:得到令牌
ResourceController:資源服務
ClientController:客戶端web
基礎代碼放到github:https://github.com/zhouyongtao/homeinns-webspring
確保項目8080端口運行,能夠手動調試apache
得到受權碼
http://localhost:8080/oauth2/authorize?client_id=fbed1d1b4b1449daa4bc49397cbe2350&response_type=code&redirect_uri=http://localhost:8080/oauth_callback
得到令牌(POST)
http://localhost:8080/oauth2/access_token?client_id=fbed1d1b4b1449daa4bc49397cbe2350&client_secret=fbed1d1b4b1449daa4bc49397cbe2350&grant_type=authorization_code&redirect_uri=http://localhost:8080/oauth_callback&code={code}json
也能夠使用以下客戶端測試代碼,訪問 http://localhost:8080/client 測試api
/** * Created by Irving on 2014/11/24. * OAuth2 客戶端實現 */ @Controller @RequestMapping("/client") public class ClientController { private static Logger logger = LoggerFactory.getLogger(ClientController.class); /* response_type:表示受權類型,必選項,此處的值固定爲"code" client_id:表示客戶端的ID,必選項 redirect_uri:表示重定向URI,可選項 scope:表示申請的權限範圍,可選項 state:表示客戶端的當前狀態,能夠指定任意值,認證服務器會原封不動地返回這個值 */ /** * 得到受權碼 * @return */ @RequestMapping(method = RequestMethod.GET) public String client() { try { OAuthClientRequest oauthResponse = OAuthClientRequest .authorizationLocation(ConstantKey.OAUTH_CLIENT_AUTHORIZE) .setResponseType(OAuth.OAUTH_CODE) .setClientId(ConstantKey.OAUTH_CLIENT_ID) .setRedirectURI(ConstantKey.OAUTH_CLIENT_CALLBACK) .setScope(ConstantKey.OAUTH_CLIENT_SCOPE) .buildQueryMessage(); return "redirect:"+oauthResponse.getLocationUri(); } catch (OAuthSystemException e) { e.printStackTrace(); } return "redirect:/home"; } /* grant_type:表示使用的受權模式,必選項,此處的值固定爲"authorization_code" code:表示上一步得到的受權碼,必選項。 redirect_uri:表示重定向URI,必選項,且必須與A步驟中的該參數值保持一致 client_id:表示客戶端ID,必選項 */ /** * 得到令牌 * @return oauth_callback?code=1234 */ @RequestMapping(value = "/oauth_callback" ,method = RequestMethod.GET) public String getToken(HttpServletRequest request,Model model) throws OAuthProblemException { OAuthAuthzResponse oauthAuthzResponse = null; try { oauthAuthzResponse = OAuthAuthzResponse.oauthCodeAuthzResponse(request); String code = oauthAuthzResponse.getCode(); OAuthClientRequest oauthClientRequest = OAuthClientRequest .tokenLocation(ConstantKey.OAUTH_CLIENT_ACCESS_TOKEN) .setGrantType(GrantType.AUTHORIZATION_CODE) .setClientId(ConstantKey.OAUTH_CLIENT_ID) .setClientSecret(ConstantKey.OAUTH_CLIENT_SECRET) .setRedirectURI(ConstantKey.OAUTH_CLIENT_CALLBACK) .setCode(code) .buildQueryMessage(); OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient()); //Facebook is not fully compatible with OAuth 2.0 draft 10, access token response is //application/x-www-form-urlencoded, not json encoded so we use dedicated response class for that //Custom response classes are an easy way to deal with oauth providers that introduce modifications to //OAuth 2.0 specification //獲取access token OAuthJSONAccessTokenResponse oAuthResponse = oAuthClient.accessToken(oauthClientRequest, OAuth.HttpMethod.POST); String accessToken = oAuthResponse.getAccessToken(); String refreshToken= oAuthResponse.getRefreshToken(); Long expiresIn = oAuthResponse.getExpiresIn(); //得到資源服務 OAuthClientRequest bearerClientRequest = new OAuthBearerClientRequest(ConstantKey.OAUTH_CLIENT_GET_RESOURCE) .setAccessToken(accessToken).buildQueryMessage(); OAuthResourceResponse resourceResponse = oAuthClient.resource(bearerClientRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class); String resBody = resourceResponse.getBody(); logger.info("accessToken: "+accessToken +" refreshToken: "+refreshToken +" expiresIn: "+expiresIn +" resBody: "+resBody); model.addAttribute("accessToken", "accessToken: "+accessToken + " resBody: "+resBody); return "oauth2/token"; } catch (OAuthSystemException ex) { logger.error("getToken OAuthSystemException : " + ex.getMessage()); model.addAttribute("errorMsg", ex.getMessage()); return "/oauth2/error"; } } }
Refer:
https://cwiki.apache.org/confluence/display/OLTU/Index
https://open.weixin.qq.com/cgi-bin/readtemplate?t=resource/app_wx_login_tmpl&lang=zh_CN#faq服務器