admin 管理员组文章数量: 1086019
最新更新时间:2019年06月10日17:23:05
《猛戳-查看我的博客地图-总有你意想不到的惊喜》
本文内容:前后端开发分离,如何实现用户登录状态监控和校验,要解决的几个问题:
- A用户登录A机器成功,此时A用户登录B机器,A机器的A用户登录状态需要下线,即保证一个用户只能同时在线一台机器;
- A用户登录成功,一周之内不需要再次登录;
实现原理
- 用户首次访问站点,后端从请求体中读取cookie,此时cookie中的token为空,用户需要进行登录;
- 用户首次登录成功,后端的response向浏览器写入cookie,在cookie中存储该用户唯一的并且有时效性的token;
- 用户二次访问站点,如果token没有失效(过期或更新,过期:是指我们设定的一周的有效期;更新:是指用户在其他设备/浏览器登录后,redis数据库会更新token),不做处理;如果失效,重新设定路由指向登录页,引导用户重新登录;
- 如果用户退出操作,调用退出接口,后端的response会主动删除浏览器中的cookie;
概述
常规的PC站点开发,如果有访问权限控制,需要用户登录才能访问,因此合理安全的方案至关重要,本文方案的具体实现过程如下:
- 前端请求后端的每一个接口,需要对接口返回值的code码进行分类处理,我们定义,在任意页面的任意接口返回值code码为11115时,表示用户登录身份失效,此时需要重新登录,用路由控制直接跳转到登录页面;
- 后端在接收任意一个请求时,需要在Spring MVC拦截器
Interceptor
中做处理,对用户登录状态进行判断,读取浏览器中的cookie和redis数据库进行对比,如果cookie中的token有效,返回正常结果,如果token失效,返回11115 code码;
前端代码
fetch().then((Response)=>{
let data = Response.json();//res是一个json文件,使用 json() 读取并解析数据,返回一个被解析为JSON格式的promise对象
data.then((res,rej)=>{
if(res.code == 100){
}else if(res.code == 11115){
//用户登录失效,token失效,跳转到登录页面
history.replace('/login')
window.location.reload()
}else{
}
})
}).catch((e)=>{
})
后端代码
每个接口的拦截,使用
Interceptor
拦截器
//HandlerInterceptor接口
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;
void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
}
//HandlerInterceptor接口的实现
public class AuthInterceptor implements HandlerInterceptor {
//引入 声明
@Autowired
private RedisManage redisManage;
//重写
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
String Origin = request.getHeader("Origin");
if(Origin != null){
response.setHeader("Access-Control-Allow-Origin",Origin);
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type, Accept-Language, Origin, Accept-Encoding");
}
String url = request.getRequestURI();
boolean isLogout = false;
String token = "";
Cookie[] cookies = request.getCookies();
if(cookies == null) {
isLogout = true;
}else {
token = cookies.getName();//没有这个方法cookies.getName(),此处举个例子
}
if(token == ‘’) {
isLogout = true;
}else {
//查询redis数据库
if(redisManage.getValue(token) == null){
isLogout = true;
}else{
String tokenInRedis = (String) redisManage.getValue(token);
if(tokenInRedis != token) {
isLogout = true;
}
}
}
if(isLogout) {
//清空session
request.getSession().invalidate();
//清空cookies信息
Cookie tokenInRedis = new Cookie("token","");
tokenInRedis.setMaxAge(0);
tokenInRedis.setHttpOnly(true);
//响应中写入cookie
response.addCookie(tokenInRedis);
ServiceResult serviceResult = new ServiceResult(60*60*24*7);
response.getWriter().write(JSONObject.toJSONString(serviceResult));
response.getWriter().flush();
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
登录 类
public ServiceResult login(@RequestBody User user, HttpServletRequest request , HttpServletResponse response){
ServiceResult serviceResult = userService.login(user);
String userName = 'wanshaobo';
if(serviceResult.getCode() == 100){
String md5Token = MD5Util.md5(userName + U2AESKey + "REMIXED" + Math.random());
Cookie cookie01 = new Cookie("token",md5Token);
//7天过期时间,单位为秒
U2TokenCookie.setMaxAge(60*60*24*7);
U2TokenCookie.setHttpOnly(true);
U2TokenCookie.setPath("/");
response.addCookie(cookie01);
//存储token到redis
redisManage.putValueExpireTimes(userName,md5Token,60*60*24*7L);
}
return serviceResult;
}
登出 类
public ServiceResult logout(HttpServletRequest request,HttpServletResponse response) {
String token = "";
//清空cookies信息
Cookie[] cookies = request.getCookies();
for (Cookie coo : cookies) {
if ("token" == coo.getName()) {
coo.setMaxAge(0);
response.addCookie(coo);
}
}
//清空redis中的token
redisManage.delValue(token);
return new ServiceResult(HttpResultEnum.SUCCESS);
}
参考资料
- 无
感谢阅读,欢迎评论^-^
打赏我吧^-^
版权声明:本文标题:前后端分离之用户登录状态管理和校验 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.roclinux.cn/b/1738291691a1958043.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论