撸一个基于VUE的WEB管理后台(三)
通过上一片文章的分析,我们大致可以了解到前端项目是采用token机制来实现用户认证的。关于token认证的相关机制已经有很多资料进行论述,就实现机制来看,可以分为几大类:
- 类似我们熟悉的session机制,服务端在缓存中保留token的所有状态,每次请求都去缓存中检查token状态。这个方案中服务端和前端使用的token只需一个ID(用于标识服务端缓存中token的索引)即可
- refresh token机制,这个方案中token自身通过加密的方式存储了用户的身份信息以及过期时间等信息,每次请求服务端通过解析token获取用户身份信息,当token过期反馈给前端,前端通过refresh token接口与服务端一起刷新token数据。这便是无状态token方案。
- 还有适用于认证服务、应用服务以及用户之间相互不信任的OAuth认证机制。
以上方案各有优缺点,也有各自的适用场景,因为我们平台数据和用户并不多,短期内也没有横向扩展需求,也没有单点登录的需要,为了简便,我更倾向于采用session机制来实现。
SpringBoot内置的HttpServletRequest就包含有简单的session管理,但他默认的session是基于cookie的,而我们前端是采用http header发送token的,为了让服务端支持header的方式接受session token,就需要我们定制一下session管理器了。
基于HTTP HEADER传输SESSION ID
为了实现从header获取session id,我们可以自己注册一个Filter,在这个Filter里用我们自定义的HttpServletRequestWrapper将HttpServletRequest进行包装,将其所有与Session相关的接口重写,这样在后面各类接口使用request.getSession()时就会调用我们自己实现的Session接口。
然而,我们知道,使用Spring框架最大的好处就是,几乎所有WEB应用开发时你想要的功能,他即使暂时没有实现,也帮你考虑到了。Spring Session 就解决了我们的这个问题。来自Spring Session官网的一段介绍:
HttpSession - allows replacing the HttpSession in an application container (i.e. Tomcat) neutral way, with support for providing session IDs in headers to work with RESTful APIs
得益于Spring的良好设计,有兴趣的可以去看Spring Session是如何解决这个问题的 https://docs.spring.io/spring-session/docs/2.2.2.RELEASE/reference/html5/#httpsession-how
本质上Spring Session应该和我前面提到的自己实现一套Wrapper的原理是一样的,只是我们不再需要重复造*了。
我们来到 pom.xml 为后端项目加上spring-session-core这个模块。
<dependencies>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
</dependency>
大概了解一下SpringHttpSessionConfiguration,然后阅读一下spring session的文档,可以知道直接使用session包里赠送的HeaderHttpSessionIdResolver和MapSessionRepository,就可以达到我们的目的,我们新建一个配置类SessionConfiguration 类配置它,注意我们前端传Token用的是X-Token这个header。HeaderHttpSessionIdResolver默认为“X-Auth-Token”
package com.zy.report.config;
//省略各种 import...
@EnableSpringHttpSession
public class SessionConfiguration {
@Bean
public HttpSessionIdResolver httpSessionStrategy() {
return new HeaderHttpSessionIdResolver("X-Token");
}
@Bean
public SessionRepository repository() {
return new MapSessionRepository(new ConcurrentHashMap<>());
}
}
然后为Controller中的api添加一点内容,login这块,加上session的初始化,模拟从数据库中取出了用户名字和权限,并将之存入session对象
public ApiResult login(HttpServletRequest request, @RequestBody ReqLogin params) {
if(!(params.getUsername().equals("admin") && params.getPassword().equals("111111")))
return new ApiResult(20001, null, "用户名或密码不正确");
HttpSession session = request.getSession(true);
session.setAttribute("name", "管理员");
session.setAttribute("role", "admin");
System.out.println("New Token: " + session.getId());
Map<String, Object> r = new HashMap<>();
r.put("token", session.getId());
return new ApiResult(20000, r);
}
info接口这块,从session中获取用户名字和权限,将其返回给前端
public ApiResult info(HttpServletRequest request){
HttpSession session = request.getSession();
if(session == null)
return new ApiResult(20001, null, "无效的token");
System.out.println("Your token: " + session.getId() + ", and your name is: " +
session.getAttribute("name") + " and role is: " + session.getAttribute("role"));
Map<String, Object> r = new HashMap<>();
r.put("username", "111111");
r.put("name", session.getAttribute("name"));
List<String> roles = new ArrayList<>();
roles.add(session.getAttribute("role").toString());
r.put("roles", roles);
return new ApiResult(20000, r);
}
最后把前端项目 api/user.js 中 /user/info接口多余的调用参数去掉。
export function getInfo(token) {
return request({
url: '/user/info',
method: 'get',
//params: { token }
})
}
将浏览器里的cookie清除,再次访问 http://localhost:9527/report,使用默认用户名密码登录,结果显示登陆成功,前端进入dashboard页面,api server的终端输出为
New Token: d4595618-11a7-4023-bbd6-44670b76dadd
Your token: d4595618-11a7-4023-bbd6-44670b76dadd, and your name is: 管理员 and role is: admin
这两行分别由login和info接口输出,符合预期,再来看一下浏览器的Network记录
login请求的头部与数据
POST /user/login HTTP/1.1
Host: localhost:10200
Connection: keep-alive
#部分省略
Sec-Fetch-Mode: cors
Referer: http://localhost:9527/report
{"username":"admin","password":"111111"}
login请求的响应头与数据
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
Access-Control-Allow-Origin: *
X-Token: d4595618-11a7-4023-bbd6-44670b76dadd
#部分省略
Connection: keep-alive
{"code":20000,"data":{"token":"d4595618-11a7-4023-bbd6-44670b76dadd"},"message":null}
info请求的头部与数据
Accept: application/json, text/plain, */*
Accept-Encoding: gzip, deflate, br
#省略
X-Token: d4595618-11a7-4023-bbd6-44670b76dadd
info请求的响应头与数据
HTTP/1.1 200
Vary: Origin
Vary: Access-Control-Request-Method
Vary: Access-Control-Request-Headers
#省略
Connection: keep-alive
{"code":20000,"data":{"roles":["admin"],"name":"管理员","username":"111111"},"message":null}
可以看到,前端与api server的确已经采用X-Token的header来交互token数据了。
上一篇: django之models详讲
下一篇: aliplayer隐藏工具条
推荐阅读
-
我的第一个python web开发框架(36)——后台菜单管理功能
-
asp.net core web api + Element-UI的Vue管理后台
-
基于Vue的电商后台管理系统
-
「免费开源」基于Vue和Quasar的前端SPA项目crudapi后台管理系统实战之文件上传(十)
-
web基于后台管理模版实现高扩展的侧边栏
-
dwz - 开发一个基于PHP 的后台管理系统,相当于一个招生系统的界面,有什么推荐的吗?
-
撸一个基于VUE的WEB管理后台(三)
-
我的第一个python web开发框架(36)——后台菜单管理功能
-
我和小美的撸码日记(2)之第一个基于MVC+Jqgrid的列表页面_html/css_WEB-ITnose
-
我和小美的撸码日记(2)之第一个基于MVC+Jqgrid的列表页面_html/css_WEB-ITnose