基于Oauth2.0协议授权码模式的认证流程
授权码模式工作流程:
- 浏览器访问某个受保护的资源,客户端自动将网页重定向到认证服务器(/oauth/authorize),携带clientid等信息
- 用户选择是否给予客户端授权(可自动授权)
- 认证服务器将浏览器重定向到”重定向URI”(redirection URI),同时附上一个授权码
- 浏览器拿到授权码,附上早先的”重定向URI”,向认证服务器申请令牌(/oauth/token)
- 认证服务器核对了授权码和重定向URI,向客户端发送访问令牌(access token)和更新令牌(refresh token)
步骤1中,客户端申请认证的URI,包含以下参数:
- response_type:表示授权类型,必选项,此处的值固定为”code”
- client_id:表示客户端的ID,必选项
- redirect_uri:表示重定向URI,可选项
- scope:表示申请的权限范围,可选项
- state:一般随机生成,标识客户端的当前状态,认证服务器会原样返回这个值,
纯前端项目中获取token
通过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--------------------------
- 作者:luangeng
- 主页:https://www.code996.cn
- 本文出处:https://www.code996.cn/post/2018/token-front/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
浙公网安备 33010602011771号