基于Oauth2.0协议授权码模式的认证流程

授权码模式工作流程:

  1. 浏览器访问某个受保护的资源,客户端自动将网页重定向到认证服务器(/oauth/authorize),携带clientid等信息
  2. 用户选择是否给予客户端授权(可自动授权)
  3. 认证服务器将浏览器重定向到”重定向URI”(redirection URI),同时附上一个授权码
  4. 浏览器拿到授权码,附上早先的”重定向URI”,向认证服务器申请令牌(/oauth/token)
  5. 认证服务器核对了授权码和重定向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

  1.  
    const FULL_CHARTER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopgrstuvwxyz';
  2.  
    const oauth_server='http://localhost:9000/server';
  3.  
    const redirect_uri='http://localhost:8002/client-front/';
  4.  
    const client_id='demo';
  5.  
    const client_secret='demo';
  6.  
    const token_storage = localStorage;//sessionStorage
  7.  
     
  8.  
     
  9.  
    function ajaxSetup() {
  10.  
    $.ajaxSetup({
  11.  
    timeout : 30000,
  12.  
    beforeSend : function(xhr) {
  13.  
    if(this.url.endsWith('/oauth/token')){
  14.  
    return true;
  15.  
    }
  16.  
    if (getAuth() == null){
  17.  
    fetchToken();
  18.  
    }
  19.  
    var auth = getAuth();
  20.  
    if(auth != null){
  21.  
    xhr.setRequestHeader("Authorization", auth.token_type + ' ' + auth.access_token);
  22.  
    } else {
  23.  
    return false;
  24.  
    }
  25.  
    return true;
  26.  
    },
  27.  
    complete : function(xhr, ts) {
  28.  
    if (xhr.status == 401 && xhr.responseJSON.error =='invalid_token') {
  29.  
    refreshToken();
  30.  
    }
  31.  
    }
  32.  
    });
  33.  
    }
  34.  
     
  35.  
    function getAuth(){
  36.  
    let auth = token_storage.getItem('auth');
  37.  
    return JSON.parse(auth);
  38.  
    }
  39.  
     
  40.  
    function saveAuth(sResponse){
  41.  
    token_storage.setItem("auth", JSON.stringify(sResponse));
  42.  
    }
  43.  
     
  44.  
    function clearAuth(){
  45.  
    token_storage.removeItem('auth');
  46.  
    }
  47.  
     
  48.  
    function logout(){
  49.  
    token_storage.removeItem('auth');
  50.  
    window.location.href = oauth_server+"/logout?redirect_uri="+redirect_uri;
  51.  
    }
  52.  
     
  53.  
    function getCode(){
  54.  
    var state='';
  55.  
    for (var a=0;a<6;a++){
  56.  
    state+=FULL_CHARTER[Math.floor(Math.random() * 52)];
  57.  
    }
  58.  
    var url = oauth_server+"/oauth/authorize?client_id="+client_id+"&client_secret="+client_secret+
  59.  
    "&response_type=code&state="+state+"&redirect_uri="+redirect_uri;
  60.  
    window.location = url;
  61.  
    //window.open(url);
  62.  
    }
  63.  
     
  64.  
    function fetchToken(){
  65.  
    let url = window.location.toString();
  66.  
    if(!url.includes('code')){
  67.  
    getCode();
  68.  
    }
  69.  
    if(url.includes('code')) {
  70.  
    let code=url.substr(url.indexOf('code=')+5,6);
  71.  
    let state=url.substr(url.indexOf('state=')+6,6);
  72.  
     
  73.  
    var data={
  74.  
    'code':code,
  75.  
    'state':state,
  76.  
    'grant_type':'authorization_code',
  77.  
    'redirect_uri':redirect_uri
  78.  
    };
  79.  
    $.ajax({
  80.  
    url: oauth_server+"/oauth/token",
  81.  
    type:"post",
  82.  
    data:data,
  83.  
    async: false,
  84.  
    contentType: 'application/x-www-form-urlencoded',
  85.  
    beforeSend:function(xhr){
  86.  
    xhr.setRequestHeader("Authorization", 'Basic ' + Base64.encode(client_id+':'+client_secret));
  87.  
    },
  88.  
    success: function (sResponse) {
  89.  
    saveAuth(sResponse);
  90.  
    console.log('fetch_token ok: ' + sResponse.access_token+' expires_in:'+sResponse.expires_in);
  91.  
    //window.location.href = redirect_uri;
  92.  
    },
  93.  
    error:function(a,b,c){
  94.  
    console.log(a, b, c);
  95.  
    }
  96.  
    });
  97.  
    }
  98.  
    }
  99.  
     
  100.  
    function refreshToken(){
  101.  
    var auth = getAuth();
  102.  
    var data={
  103.  
    'client_id': client_id,
  104.  
    'client_secret': client_secret,
  105.  
    'grant_type':'refresh_token',
  106.  
    'refresh_token':auth.refresh_token
  107.  
    };
  108.  
    $.ajax({
  109.  
    url: oauth_server+"/oauth/token",
  110.  
    type:"post",
  111.  
    data:data,
  112.  
    async: false,
  113.  
    contentType: 'application/x-www-form-urlencoded',
  114.  
    success: function (sResponse) {
  115.  
    saveAuth(sResponse);
  116.  
    console.log('refresh_token ok: ' + sResponse.access_token+' expires_in:'+sResponse.expires_in);
  117.  
    },
  118.  
    error:function(a,b,c){
  119.  
    if (a.status==400 && a.responseJSON.error=='invalid_grant'){
  120.  
    console.log('refresh token invalid');
  121.  
    clearAuth();
  122.  
    }
  123.  
    }
  124.  
    });
  125.  
    }
  126.  
     
  127.  
    function checkToken(){
  128.  
    $.ajax({
  129.  
    url: oauth_server+"/oauth/check_token",
  130.  
    type:"get",
  131.  
    async: false,
  132.  
    data: {'token': getAuth().access_token},
  133.  
    contentType: 'application/x-www-form-urlencoded',
  134.  
    success: function (sResponse) {
  135.  
    console.log('check_token : ' + sResponse);
  136.  
    },
  137.  
    error:function(a,b,c){
  138.  
    console.log(a.responseJSON);
  139.  
    }
  140.  
    });
  141.  
    }
  142.  
     
  143.  
    function getServerdata(){
  144.  
    $.get(oauth_server+"/msg", function(data) {
  145.  
    $("#user").html(data);
  146.  
    });
  147.  
    }
  148.  
     
  149.  
     
  150.  
    $(function() {
  151.  
    ajaxSetup();
  152.  
    });

其中使用了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--------------------------

posted on 2019-02-22 16:28  C#小学徒  阅读(451)  评论(0)    收藏  举报