实现 OAuth2.0 服务端

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

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

  • Java
  • NodeJS
  • 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失效的后,需要用户重新授权。

    请求授权码

    /**
     * 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

    时间: 2024-11-07 17:23:02

    实现 OAuth2.0 服务端的相关文章

    创建自己的OAuth2.0服务端(一)

    如果对OAuth2.0有任何的疑问,请先熟悉OAuth2.0基础的文章:http://www.cnblogs.com/alunchen/p/6956016.html 1. 前言 本篇文章时对 客户端的授权模式-授权码模式 的创建,当然你理解的最复杂的模式之后,其他模式都是在授权码模式上面做一些小改动即可.对于授权码模式有任何的疑问,请看上面提到的文章. 注意:本文是创建OAuth的Server端,不是Client请求端. 2. 开始授权码模式的概念.流程 第2点其实就是复制了上一篇文章,为了提高

    写个OAuth2.0的请求端来测试自己的OAuth2.0服务端(二)

    在上一篇文章中,我们介绍了怎么创建自己的服务器,现在我们开始写个client端,来测试. 我们创建一个MVC项目,叫TestOAuthClient 1. 代码开始 1)第一步,我们创建一个MainController,在Index方法里面写我们的逻辑. 2)首先获取code,如果没有code,则证明是第一步请求. 3)第一步请求,附上client_id.response_type.redirect_uri.scope.state参数. 这里我们假如服务端的第一步请求认证的地址为:http://l

    Vue 2.0 服务端渲染入门

    1 什么是服务端渲染 SSR server side render 就是通过后端吐模板,而不是通过前端ajax获取数据,拼接字符串. 2 为什么需要SSR 需要SEO,因为爬虫不会等待ajax结果. 客户端网络慢,加载速度慢,影响用户体验. 3 另一种解决办法 预渲染 不是一次性下载整个单页应用,预渲染只是在构建时为了特定的路由生成特定的几个静态页面 你用webpack可以很简单地通过prerender-spa-plugin来添加预渲染 4 NodeJS编写Vue的SSR 首先npm insta

    Swift3.0服务端开发(二) 静态文件添加、路由配置以及表单提交

    今天博客中就来聊一下Perfect框架的静态文件的添加与访问,路由的配置以及表单的提交.虽然官网上有聊静态文件的访问的部分,但是在使用Perfect框架来访问静态文件时还是有些点需要注意的,这些关键点在其官方文档上并未提出.今天我们要做的事情就是通过浏览器访问静态文件,然后在静态文件中使用form表单往指定的路由上进行提交相应的数据. 一.静态文件的添加与访问 1.未使用Xcode管理的Perfect的静态文件根目录的配置 在PHP开发或者Java Web开发中,都有一个根目录来存储相应的静态文

    centos 7 上zabbix 3.0 服务端安装

    zabbix服务端安装 安装完毕mysql-5.6.php5.6 mysql-5.6安装:https://www.cnblogs.com/xzlive/p/9771642.html  创建zabbix 用户 # groupadd zabbix # useradd -g zabbix zabbix 1.1 下载安装zabbix所有版本下载地址:http://www.zabbix.com/download.php # yum install net-snmp-devel libxml2-devel

    Centos7 安装zabbix3.0 服务端 详细

    参考: https://www.cnblogs.com/37yan/p/6879218.html http://blog.csdn.net/hao134838/article/details/57122516 http://blog.csdn.net/u014057054/article/details/66476990 1.导入源 sudo rpm -ivh http://repo.zabbix.com/zabbix/3.0/rhel/7/x86_64/zabbix-release-3.0-1

    linux centos安装zabbix 4.0服务端

    1.服务器安装docker sudo yum install -y yum-utils device-mapper-persistent-data lvm2 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sudo yum makecache fast sudo yum -y install docker-ce sudo systemctl sta

    使用微服务架构思想,设计部署OAuth2.0授权认证框架

    1,授权认证与微服务架构 1.1,由不同团队合作引发的授权认证问题 去年的时候,公司开发一款新产品,但人手不够,将B/S系统的Web开发外包,外包团队使用Vue.js框架,调用我们的WebAPI,但是这些WebAPI并不在一台服务器上,甚至可能是第三方提供的WebAPI.同时处于系统安全的架构设计,后端WebAPI是不能直接暴露在外面的:另一方面,我们这个新产品还有一个C/S系统,C端登录的时候,要求统一到B/S端登录,可以从C端无障碍的访问任意B/S端的页面,也可以调用B/S系统的一些API,

    使用Owin中间件搭建OAuth2.0认证授权服务器

    前言 这里主要总结下本人最近半个月关于搭建OAuth2.0服务器工作的经验.至于为何需要OAuth2.0.为何是Owin.什么是Owin等问题,不再赘述.我假定读者是使用Asp.Net,并需要搭建OAuth2.0服务器,对于涉及的Asp.Net Identity(Claims Based Authentication).Owin.OAuth2.0等知识点已有基本了解.若不了解,请先参考以下文章: MVC5 - ASP.NET Identity登录原理 - Claims-based认证和OWIN