首页 > 代码库 > 实现 OAuth2.0 服务端

实现 OAuth2.0 服务端

要实现OAuth服务端,我觉的就得先理解客户端的调用流程,服务提供商实现可能也有些区别,实现OAuth服务端的方式很多,具体可能看 http://oauth.net/code/

各语言的实现有(我使用了Apache Oltu):

  • Java
    • Apache Oltu
    • Spring Security for OAuth
    • Apis Authorization Server (v2-31)
    • Restlet Framework (draft 30)
    • Apache CXF
  • 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
    • Ruby OAuth2 Server (draft 18)
  • .NET
    • .NET DotNetOpenAuth
    • Thinktecture IdentityServer
  • 实现主要涉及参数配置如下:
    授权码设置(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失效的后,需要用户重新授权。

    请求授权码

    /** * Authorization Code 授权码模式 * Created by Irving on 2014/11/22. * Impl OAth2  http://oauth.net/2/ */@Controller@RequestMapping("/oauth2")public class AuthzController {    private static Logger logger = LoggerFactory.getLogger(AuthzController.class);    private Cache cache ;    @Autowired    public AuthzController(CacheManager cacheManager) {        this.cache = cacheManager.getCache("oauth2-cache");    }     /* *     * 构建OAuth2授权请求 [需要client_id与redirect_uri绝对地址]     * @param request     * @param session     * @param model     * @return 返回授权码(code)有效期10分钟,客户端只能使用一次[与client_id和redirect_uri一一对应关系]     * @throws OAuthSystemException     * @throws IOException     * @url  http://localhost:8080/oauth2/authorize?client_id={AppKey}&response_type=code&redirect_uri={YourSiteUrl}     * @test http://localhost:8080/oauth2/authorize?client_id=fbed1d1b4b1449daa4bc49397cbe2350&response_type=code&redirect_uri=http://baidu.comx     */    @RequestMapping(value = "/authorize")    public String authorize(HttpServletRequest request,HttpSession session,Model model)            throws OAuthSystemException, IOException {        try {            //构建OAuth请求            OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);            //验证redirecturl格式是否合法 (8080端口测试)            if (!oauthRequest.getRedirectURI().contains(":8080")&&!Pattern.compile("^[a-zA-Z]+://(\\w+(-\\w+)*)(\\.(\\w+(-\\w+)*))*(\\?\\s*)?$").matcher(oauthRequest.getRedirectURI()).matches()) {                OAuthResponse oauthResponse = OAuthASResponse                                              .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)                                              .setError(OAuthError.CodeResponse.INVALID_REQUEST)                                              .setErrorDescription(OAuthError.OAUTH_ERROR_URI)                                              .buildJSONMessage();                logger.error("oauthRequest.getRedirectURI() : " + oauthRequest.getRedirectURI() + " oauthResponse.getBody() : " + oauthResponse.getBody());                model.addAttribute("errorMsg", oauthResponse.getBody());                return "/oauth2/error";            }            //验证appkey是否正确            if (!validateOAuth2AppKey(oauthRequest)){                OAuthResponse oauthResponse = OAuthASResponse                                              .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)                                              .setError(OAuthError.CodeResponse.ACCESS_DENIED)                                              .setErrorDescription(OAuthError.CodeResponse.UNAUTHORIZED_CLIENT)                                              .buildJSONMessage();                logger.error("oauthRequest.getRedirectURI() : "+oauthRequest.getRedirectURI()+" oauthResponse.getBody() : "+oauthResponse.getBody());                model.addAttribute("errorMsg", oauthResponse.getBody());                return "/oauth2/error";            }            //查询客户端Appkey应用的信息            String clientName= "Just Test App";//oauthClientService.findByClientId(oauthRequest.getClientId());            model.addAttribute("clientName",clientName);            model.addAttribute("response_type",oauthRequest.getResponseType());            model.addAttribute("client_id",oauthRequest.getClientId());            model.addAttribute("redirect_uri",oauthRequest.getRedirectURI());            model.addAttribute("scope",oauthRequest.getScopes());            //验证用户是否已登录            if(session.getAttribute(ConstantKey.MEMBER_SESSION_KEY)==null) {                //用户登录                if(!validateOAuth2Pwd(request)) {                    //登录失败跳转到登陆页                    return "/oauth2/login";                }            }            //判断此次请求是否是用户授权            if(request.getParameter("action")==null||!request.getParameter("action").equalsIgnoreCase("authorize")){                //到申请用户同意授权页                return "/oauth2/authorize";            }           //生成授权码 UUIDValueGenerator OR MD5Generator           String authorizationCode = new OAuthIssuerImpl(new MD5Generator()).authorizationCode();           //把授权码存入缓存           cache.put(authorizationCode, DigestUtils.sha1Hex(oauthRequest.getClientId()+oauthRequest.getRedirectURI()));           //构建oauth2授权返回信息           OAuthResponse oauthResponse = OAuthASResponse                                         .authorizationResponse(request,HttpServletResponse.SC_FOUND)                                         .setCode(authorizationCode)                                         .location(oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI))                                         .buildQueryMessage();           //申请令牌成功重定向到客户端页           return "redirect:"+oauthResponse.getLocationUri();        } catch(OAuthProblemException ex) {            OAuthResponse oauthResponse = OAuthResponse                                          .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)                                          .error(ex)                                          .buildJSONMessage();            logger.error("oauthRequest.getRedirectURI() : " + ex.getRedirectUri() + " oauthResponse.getBody() : " + oauthResponse.getBody());            model.addAttribute("errorMsg", oauthResponse.getBody());            return  "/oauth2/error";        }    }    /**     * 用户登录     * @param request     * @return     */    private boolean validateOAuth2Pwd(HttpServletRequest request) {        if("get".equalsIgnoreCase(request.getMethod())) {            return false;        }        String name = request.getParameter("name");        String pwd = request.getParameter("pwd");        if(StringUtils.isEmpty(name) || StringUtils.isEmpty(pwd)) {            return false;        }        try {            if(name.equalsIgnoreCase("Irving")&&pwd.equalsIgnoreCase("123456")){                //登录成功                request.getSession().setAttribute(ConstantKey.MEMBER_SESSION_KEY,"Irving");                return true;            }            return false;        } catch (Exception ex) {            logger.error("validateOAuth2Pwd Exception: " + ex.getMessage());            return false;        }    }    /**     * 验证ClientID 是否正确     * @param oauthRequest     * @return     */    public boolean validateOAuth2AppKey(OAuthAuthzRequest oauthRequest) {        //客户端Appkey        ArrayList arrayKeys = new  ArrayList();        arrayKeys.add("fbed1d1b4b1449daa4bc49397cbe2350");        arrayKeys.add("a85b033590714fafb20db1d11aed5497");        arrayKeys.add("d23e06a97e2d4887b504d2c6fdf42c0b");        return arrayKeys.contains(oauthRequest.getClientId());    }}

    Refer:https://open.weixin.qq.com/cgi-bin/readtemplate?t=resource/app_wx_login_tmpl&lang=zh_CN#faq

    实现 OAuth2.0 服务端