授权服务如何生成访问令牌?
访问令牌过期了而用户又不在场的情况下,又如何重新生成访问令牌?
授权服务的工作过程
在 xx让我去公众号开放平台给它授权数据时,你是否好奇?开放平台怎么知道 xx 是谁?他合法备案了吗?万一是个病毒软件咋办?
所以,授权的前提是xx要去开放平台备案,即注册。之后,开放平台就会给xx软件app_id和app_secret等,方便后面授权时的各种校验。
注册时,三方软件也会请求受保护资源的可访问范围。比如,xx能否获取我的公众号半年前的文章,能否获取每个文章的所有信息(比如标题、封面、标签)等。即scope。
注册后,xx过来让平台把我的文章数据都给xx,平台核实后确认xx合法。
咱们上节课讲过,在授权码许可类型中,授权服务的工作,可以划分为两大部分,一个是颁发授权码code,一个是颁发访问令牌access_token。
颁发授权码code流程
过程 1:颁发授权码code
授权服务负责准备工作和生成授权码code。
准备工作
包括验证基本信息、权限范围(第一次)和生成授权请求页面。
验证基本信息
Web 颁发code的整个请求过程,都是通过浏览器由前端通信完成,意味着所有信息都可能被伪造,比如回调地址,将其伪装成钓鱼页面,授权服务需要对回调地址做基本的校验。
授权服务必须对三方软件的存在性判断。
if(!appMap.get("redirect_uri").equals(redirectUri)){ //回调地址不存在 }
在授权服务的程序中,这两步验证通过后,就会生成或者响应一个页面(授权服务器上的页面),提示小明进行授权。
第二步,验证权限范围(第一次)
授权就会涉及范围。比如使用微信登录三方软件时,微信提示我们,第三方软件可获得你的昵称、头像、性别、地理位置等。如你不想让三方软件获取你的某个信息,可不选择该项。
即需要对xx传过来的scope参数,与小兔注册时申请的权限范围对比。
此刻是第一次权限校验。
String scope = request.getParameter("scope"); if(!checkScope(scope)){ //超出注册的权限范围 }
第三步,生成授权请求页面
即授权服务上的页面,页面上显示注册时申请的权限,我可以选择缩小这个权限范围。
至此,颁发授权码code准备工作完成。当用户点击授权按钮“approve”后,才会生成授权码code值和访问令牌acces_token
。
注意只有用户登录了才可对三方软件授权,授权服务才能够获得用户信息并最终生成code 和 app_id(第三方软件的应用标识) + user(资源拥有者标识)之间的对应关系。
我扫码同意后,生成授权码code的流程就开始了,主要包括验证权限范围(第二次)、处理授权请求生成授权码code和重定向至第三方软件这三大步。
第四步,验证权限范围(第二次)
步骤二生成授权页面前授权服务进行的第一次校验,是对比xx请求的权限范围和注册时的权限。
为什么又要校验一次
因为这相当于一次用户的输入权限。我选择了权限范围给授权服务,对权限的校验,凡输入性数据都会涉及合法性检查。
String[] rscope =request.getParameterValues("rscope"); if(!checkScope(rscope)){ //超出注册的权限范围 }
第五步 处理授权请求,生成授权码code
我同意授权后,授权服务会校验响应类型response_type
的值。response_type有code和token两种类型的值。
用授权码流程举例,因此代码要验证response_type的值是否为code。
String responseType = request.getParameter("response_type"); if("code".equals(responseType)){ ... }
授权服务中,需要将生成的授权码code值与app_id、user进行关系映射。即一个授权码code,表示某用户给某三方软件授权。同时要将code和这种映射关系保存,以便在生成访问令牌access_token时使用。
// 模拟登录用户为USERTEST String code = generateCode(appId,"USERTEST"); private String generateCode(String appId,String user) { ... String code = strb.toString(); codeMap.put(code,appId+"|"+user+"|"+System.currentTimeMillis()); return code; }
生成授权码code后,也按照上面所述绑定了响应的映射关系。还需要为code设置有效期。
OAuth 2.0规范建议授权码code值有效期为10分钟,并且一个授权码code只能被使用一次。生产环境code有效期一般不超过5min。
授权服务还要将生成的授权码code跟已授权的权限范围rscope进行绑定并存储,以便后续颁发访问令牌时,能够通过code值取出授权范围并与访问令牌绑定。因三方软件最终是通过访问令牌来请求受保护资源。
Map<String,String[]> codeScopeMap = new HashMap<String, String[]>(); codeScopeMap.put(code,rscope);//授权范围与授权码做绑定
第六步,重定向至三方软件
生成授权码code后,授权服务需要将该code值告知第三方软件。
颁发授权码code是前端通信完成,因此这里采用重定向。即第二次重定向
Map<String, String> params = new HashMap<String, String>(); params.put("code",code); String toAppUrl = URLParamsUtil.appendParams(redirectUri,params);//构造第三方软件的回调地址,并重定向到该地址 response.sendRedirect(toAppUrl);//授权码流程的“第二次”重定向
至此,颁发授权码code的流程全部完成。xx获取到授权码code值后,就可请求访问令牌access_token的值,即过程二。