前后端分离-根据代码讲解思路
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统一进行校验,如果是正确的进放行。