前後端分離-根據程式碼講解思路

藍星花發表於2018-10-21

1.前面寫過前後端分離相關文章

第一篇-為什麼要前後端分離 https://blog.csdn.net/m0_37499059/article/details/82082534
第二篇-感受一下前後端分離 https://blog.csdn.net/m0_37499059/article/details/82082825


原始碼下載: https://github.com/chenxingxing6/cxxfast
在這裡插入圖片描述

2.根據程式碼講解思路

使用者登入時,生成一個token,並給token設一個過期時間,儲存在資料庫裡(redis裡)。每次請求api時都帶上該token,然後對token進行校驗,是不是該使用者的,並且時間有沒有過期。這裡的token最後是放在請求頭裡面,然後設定全域性範圍起作用,不需要每次還關心要自己傳token.

2.1 token什麼時候生成(使用者授權認證成功後)

在這裡插入圖片描述

token用什麼演算法生成,看自己怎麼實現了

	@Override
	public R createToken(long userId) {
		//生成一個token
		String token = TokenGenerator.generateValue();
		//當前時間
		Date now = new Date();
		//過期時間
		Date expireTime = new Date(now.getTime() + EXPIRE * 1000);
		//判斷是否生成過token
		SysUserToken tokenEntity = sysUserTokenDao.selectById(userId);
		if(tokenEntity == null){
			tokenEntity = new SysUserToken();
			tokenEntity.setUserId(userId);
			tokenEntity.setToken(token);
			tokenEntity.setUpdateTime(now);
			tokenEntity.setExpireTime(expireTime);
			//儲存token
			sysUserTokenDao.insert(tokenEntity);
		}else{
			tokenEntity.setToken(token);
			tokenEntity.setUpdateTime(now);
			tokenEntity.setExpireTime(expireTime);
			//更新token
			sysUserTokenDao.updateById(tokenEntity);
		}
		R r = R.ok().put("token", token).put("expire", EXPIRE);
		return r;
	}

生成token

package cn.jeefast.common.oauth2;
import java.security.MessageDigest;
import java.util.UUID;
import cn.jeefast.common.exception.RRException;

/**
 * 生成token
 */
public class TokenGenerator {
    public static String generateValue() {
        return generateValue(UUID.randomUUID().toString());
    }
    private static final char[] hexCode = "0123456789abcdef".toCharArray();
    public static String toHexString(byte[] data) {
        if(data == null) {
            return null;
        }
        StringBuilder r = new StringBuilder(data.length*2);
        for ( byte b : data) {
            r.append(hexCode[(b >> 4) & 0xF]);
            r.append(hexCode[(b & 0xF)]);
        }
        return r.toString();
    }
    public static String generateValue(String param) {
        try {
            MessageDigest algorithm = MessageDigest.getInstance("MD5");
            algorithm.reset();
            algorithm.update(param.getBytes());
            byte[] messageDigest = algorithm.digest();
            return toHexString(messageDigest);
        } catch (Exception e) {
            throw new RRException("生成Token失敗", e);
        }
    }
}

2.2 前端登入接收到返回的訊息和token,然後把token保持的本地(localstorage 儲存物件)

在這裡插入圖片描述


2.3然後設定全域性js,獲取token,並設定ajax請求是head都統一帶上token

common.js

//請求字首
var baseURL = "/project/";

//登入token
var token = localStorage.getItem("token");
if(token == 'null'){
    parent.location.href = baseURL + 'login.html';
}

//jquery全域性配置
$.ajaxSetup({
	dataType: "json",
	cache: false,
    headers: {
        "token": token
    },
    complete: function(xhr) {
        //token過期,則跳轉到登入頁面
        if(xhr.responseJSON.code == 401){
            parent.location.href = baseURL + 'login.html';
        }
    }
});

//jqgrid全域性配置
$.extend($.jgrid.defaults, {
    ajaxGridOptions : {
        headers: {
            "token": token
        }
    }
});

在這裡插入圖片描述


2.4 退出登入操作,清除token
 logout: function () {
	//刪除本地token
   localStorage.removeItem("token");
   //跳轉到登入頁面
   location.href = baseURL + 'login.html';
}

2.5 Java對api進行攔截,除了不需要許可權的,去校驗token

第一種方式:用了shiro框架
在這裡插入圖片描述

第二種方式:寫一個攔截器,對token進行校驗

這裡我們自己定義一個註解,如果不要授權的api,就加上改註解

在這裡插入圖片描述

攔截器實現

@Component
public class AuthorizationInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private TbTokenService tokenService;
    public static final String USER_KEY = "userId";
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        AuthIgnore annotation;
        if(handler instanceof HandlerMethod) {
            annotation = ((HandlerMethod) handler).getMethodAnnotation(AuthIgnore.class);
        }else{
            return true;
        }
        //如果有@IgnoreAuth註解,則不驗證token
        if(annotation != null){
            return true;
        }
        //從header中獲取token
        String token = request.getHeader("token");
        //如果header中不存在token,則從引數中獲取token
        if(StringUtils.isBlank(token)){
            token = request.getParameter("token");
        }
        //token為空
        if(StringUtils.isBlank(token)){
            throw new RRException("token不能為空");
        }
        //查詢token資訊
        TbToken tokenEntity = tokenService.queryByToken(token);
        if(tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()){
            throw new RRException("token失效,請重新登入");
        }
        //設定userId到request裡,後續根據userId,獲取使用者資訊
        request.setAttribute(USER_KEY, tokenEntity.getUserId());
        return true;
    }
}

註解:

import java.lang.annotation.*;
/**
 * api介面,忽略Token驗證
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthIgnore {

}

總結

其實也很簡單嘛,就後臺提供restful介面,我前端去請求,然後解析json,絢爛我們的頁面。但是這個介面肯定要有許可權吧,不能讓每個人都調的到,所以我們要進行認證,那我們根據使用者資訊,生成一個有過期時間的唯一的token,每次請求都帶上,後臺對token統一進行校驗,如果是正確的進放行。

相關文章