基于Oauth2.0协议授权码模式
授权码模式工作流程:
(A)浏览器访问该js客户端,js将网页重定向到认证服务器(/oauth/authorize),携带clientid等信息
(B)用户选择是否给予客户端授权(可自动授权)
(C)认证服务器将浏览器重定向到"重定向URI"(redirection URI),同时附上一个授权码
(D)浏览器拿到授权码,附上早先的"重定向URI",向认证服务器申请令牌(/oauth/token)
(E)认证服务器核对了授权码和重定向URI,向客户端发送访问令牌(access token)和更新令牌(refresh token)
A步骤中,客户端申请认证的URI,包含以下参数:
- response_type:表示授权类型,必选项,此处的值固定为"code"
- client_id:表示客户端的ID,必选项
- redirect_uri:表示重定向URI,可选项
- scope:表示申请的权限范围,可选项
- state:一般随机生成,标识客户端的当前状态,认证服务器会原样返回这个值,
通过JS ajax拦截器获取token及刷新token示例,适用于前后端分离项目中前端的授权。
auth.js
const FULL_CHARTER = ‘ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopgrstuvwxyz‘; const oauth_server=‘http://localhost:9000/server‘; const redirect_uri=‘http://localhost:8002/client-front/‘; const client_id=‘demo‘; const client_secret=‘demo‘; const token_storage = localStorage;//sessionStorage function ajaxSetup() { $.ajaxSetup({ timeout : 30000, beforeSend : function(xhr) { if(this.url.endsWith(‘/oauth/token‘)){ return true; } if (getAuth() == null){ fetchToken(); } var auth = getAuth(); if(auth != null){ xhr.setRequestHeader("Authorization", auth.token_type + ‘ ‘ + auth.access_token); } else { return false; } return true; }, complete : function(xhr, ts) { if (xhr.status == 401 && xhr.responseJSON.error ==‘invalid_token‘) { refreshToken(); } } }); } function getAuth(){ let auth = token_storage.getItem(‘auth‘); return JSON.parse(auth); } function saveAuth(sResponse){ token_storage.setItem("auth", JSON.stringify(sResponse)); } function clearAuth(){ token_storage.removeItem(‘auth‘); } function logout(){ token_storage.removeItem(‘auth‘); window.location.href = oauth_server+"/logout?redirect_uri="+redirect_uri; } function getCode(){ var state=‘‘; for (var a=0;a<6;a++){ state+=FULL_CHARTER[Math.floor(Math.random() * 52)]; } var url = oauth_server+"/oauth/authorize?client_id="+client_id+"&client_secret="+client_secret+ "&response_type=code&state="+state+"&redirect_uri="+redirect_uri; window.location = url; //window.open(url); } function fetchToken(){ let url = window.location.toString(); if(!url.includes(‘code‘)){ getCode(); } if(url.includes(‘code‘)) { let code=url.substr(url.indexOf(‘code=‘)+5,6); let state=url.substr(url.indexOf(‘state=‘)+6,6); var data={ ‘code‘:code, ‘state‘:state, ‘grant_type‘:‘authorization_code‘, ‘redirect_uri‘:redirect_uri }; $.ajax({ url: oauth_server+"/oauth/token", type:"post", data:data, async: false, contentType: ‘application/x-www-form-urlencoded‘, beforeSend:function(xhr){ xhr.setRequestHeader("Authorization", ‘Basic ‘ + Base64.encode(client_id+‘:‘+client_secret)); }, success: function (sResponse) { saveAuth(sResponse); console.log(‘fetch_token ok: ‘ + sResponse.access_token+‘ expires_in:‘+sResponse.expires_in); //window.location.href = redirect_uri; }, error:function(a,b,c){ console.log(a, b, c); } }); } } function refreshToken(){ var auth = getAuth(); var data={ ‘client_id‘: client_id, ‘client_secret‘: client_secret, ‘grant_type‘:‘refresh_token‘, ‘refresh_token‘:auth.refresh_token }; $.ajax({ url: oauth_server+"/oauth/token", type:"post", data:data, async: false, contentType: ‘application/x-www-form-urlencoded‘, success: function (sResponse) { saveAuth(sResponse); console.log(‘refresh_token ok: ‘ + sResponse.access_token+‘ expires_in:‘+sResponse.expires_in); }, error:function(a,b,c){ if (a.status==400 && a.responseJSON.error==‘invalid_grant‘){ console.log(‘refresh token invalid‘); clearAuth(); } } }); } function checkToken(){ $.ajax({ url: oauth_server+"/oauth/check_token", type:"get", async: false, data: {‘token‘: getAuth().access_token}, contentType: ‘application/x-www-form-urlencoded‘, success: function (sResponse) { console.log(‘check_token : ‘ + sResponse); }, error:function(a,b,c){ console.log(a.responseJSON); } }); } function getServerdata(){ $.get(oauth_server+"/msg", function(data) { $("#user").html(data); }); } $(function() { ajaxSetup(); });
---
其中使用了base64.js,界面如下,点击GET Data按钮将发送/msg请求到授权服务器获取一段文本。
浏览器所有请求如下:
首先访问/msg,返回401无权限,然后浏览器转到/oauth/authorize去获取code,然后授权服务器返回302重定向到授权登录页,用户填写用户名密码后post到/login,然后授权服务器重定向到rediret_url,末尾拼接了code,取出code,post发送/oauth/token请求,授权服务器返回token信息如下,并保存在LocalStorage中。
获取token后/msg请求中携带token信息,bearer为token类型
由于是跨域请求数据,所以先发送的是OPTIONS请求,需要服务器支持跨域:
刷新token:
当服务器返回401,并且responseMsg为invalid_token时表示token已失效,post以下信息到/oauth/token,刷新token的失效时间
checktoken:需要服务器permitAll()该请求
登出:
略
授权服务器中的token:
end
原文地址:https://www.cnblogs.com/luangeng/p/9346553.html