-
一、web会话跟踪
-
主要使用到的东西是token(中文翻译为令牌)
-
token是服务器端生成的一个字符串,以作为客户端向服务端进行请求的一个令牌,当第一次登录后,服务器会生成一个token并将此token返回给客户端,之后客户端只需带上这个token前来请求数据即可
-
服务器端对应每个客户端生成的唯一的token会保存在客户端,并且进行了加密,保证了数据的安全性(即每次前端请求到后端时都会先验证一下客户端的token是否有效)
-
二、登录案例的描述
- 客户端先向后端发送一个登录请求,然后服务器对登录请求中的账号和密码进行验证,如果账号和密码正确,服务器会向客户端分配一个特定的token,客户端会将响应回来的token存在浏览器中,在之后大的请求中将token放在请求头中再将请求发送给服务器,服务器再对从请求中获取到的token进行分析判断该用户是否有权限进行访问并向客户端作出一个响应将结果告诉客户端
三、登录案例的实现
-
前端向后端发送请求
1、使用npm install axios这个命令安装axios
2、在main.js中配置axios
//导入网络请求库
import axios from 'axios';//导入 axios
axios.defaults.baseURL = "http://127.0.0.1:8081/webBack/";//设置访问后台服务器地址
Vue.prototype.$http = axios;//将 axios 挂载到 vue 全局对象中,使用 this 可以直接访问
3、组装请求中的数据
this.$http.post("login",jsonToString(this.form)).then((resp)=>{
})
-
后端接收前端发来的请求
//接收登录表单数据
String account = req.getParameter("account");
String password = req.getParameter("password");
-
后端连接数据库并与数据库做一次交互
1、创建一个数据库,创建一个管理员表
create database web_db charset utf8
create table admin(
id int primary key auto_increment,
account varchar(20) unique,
password varchar(20)
)
select * from admin where account='admin' and password='111'
2、在后端中使用jdbc连接数据库
public Admin login(String account,String password) throws ClassNotFoundException, SQLException {
Connection connection=null;
PreparedStatement ps=null;
Admin admin=null;
try{
Class.forName("com.mysql.cj.jdbc.Driver");
String url="jdbc:mysql://127.0.0.1:3306/web_db?serverTimezone=Asia/Shanghai";
String user="root";
String psw="root";
connection= DriverManager.getConnection(url, user, psw);
ps = connection.prepareStatement("select id,account from admin where account=? and password=?");
ps.setObject(1, account);
ps.setObject(2, password);
ResultSet rs = ps.executeQuery();
while(rs.next()){
admin=new Admin();
admin.setId(rs.getInt("id"));
admin.setAccount(rs.getString("account"));
}
}finally {
if(connection!=null){
connection.close();
}
if(ps!=null){
ps.close();
}
}
return admin;
}
-
后端根据与数据库交互的结果向前端作出响应
-
接收到DAO(数据访问对象)返回的查询结果admin,对数据使用CommonResult类进行封装,对admin进行逻辑判断并做出相应的处理,再对DAO中抛出的异常进行处理
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收登录表单数据
String account = req.getParameter("account");
String password = req.getParameter("password");
//设置响应的编码
resp.setContentType("text/html;charset=utf-8");//对响应对象进行设置
PrintWriter printWriter=resp.getWriter();//得到一个响应对象的打印流
//后端数据封装的一个公共类
CommonResult commonResult=null;
try{
LoginDao loginDao = new LoginDao();
Admin admin = loginDao.login(account, password);
if(admin!=null){
String token= JWTUtil.getToken(admin);
admin.setToken(token);
commonResult=new CommonResult(200, admin,"登录成功");
}else{
commonResult=new CommonResult(201, "账号或密码错误");
}
}catch (Exception e){
e.printStackTrace();
commonResult=new CommonResult(500, "系统忙");
}
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(commonResult);
printWriter.write(json);
}
2、在前端将用户账号存入浏览器
//在浏览器中存储用户账号信息
sessionStorage.setItem("account",resp.data.data.account);
-
前端处理后端作出的响应
1、接收后端响应回来的数据
2、前端在符合条件时进行路由跳转
login(){
//预留与后端进行一次交互
console.log(jsonToString(this.form));
this.$http.post("login",jsonToString(this.form)).then((resp)=>{
if(resp.data.code==200){
//提示
this.$message({ showClose: true,message: resp.data.message,type: 'success' });
//在浏览器中存储用户账号信息
sessionStorage.setItem("account",resp.data.data.account);
sessionStorage.setItem("adminToken",resp.data.data.token);
//路由跳转
this.$router.push("/main");
}
if(resp.data.code==201){
this.$message({ showClose: true,message: resp.data.message,type: 'warning' });
}
if(resp.data.code==500){
this.$message({ showClose: true,message: resp.data.message,type: 'error' });
}
})
}
-
在前端通过通过路由导航来对浏览器中是否存有用户的账号信息来验证用户是否已经登录
// to-将要访问的页面地址,from-从哪个页面访问的,next-放行函数
//路由导航守卫,每次发生路由跳转时,就会自动的执行此逻辑
rout.beforeEach((to, from, next) => {
console.log(to.path)
if (to.path == '/login') { //如果用户访问的登录页,直接放行
return next();
} else {
var account= window.sessionStorage.getItem("account");//从浏览器中取出用户信息
if (account == null) {//没有登录
return next("/login");
} else {//已经登录
next();
}
}
})
-
在后端使用web会话跟踪技术通过token进一步验证用户是否已经登录
1、登录请求向后端发送账号和密码
this.$http.post("login",jsonToString(this.form))
2、后端与数据库连接验证账号和密码
public Admin login(String account,String password) throws ClassNotFoundException, SQLException {
Connection connection=null;
PreparedStatement ps=null;
Admin admin=null;
try{
Class.forName("com.mysql.cj.jdbc.Driver");
String url="jdbc:mysql://127.0.0.1:3306/web_db?serverTimezone=Asia/Shanghai";
String user="root";
String psw="root";
connection= DriverManager.getConnection(url, user, psw);
ps = connection.prepareStatement("select id,account from admin where account=? and password=?");
ps.setObject(1, account);
ps.setObject(2, password);
ResultSet rs = ps.executeQuery();
while(rs.next()){
admin=new Admin();
admin.setId(rs.getInt("id"));
admin.setAccount(rs.getString("account"));
}
}finally {
if(connection!=null){
connection.close();
}
if(ps!=null){
ps.close();
}
}
return admin;
}
3、如果密码正确,在后端生成一个唯一的令牌token,将token响应给前端
if(admin!=null){
String token= JWTUtil.getToken(admin);
admin.setToken(token);
commonResult=new CommonResult(200, admin,"登录成功");
}
4、在前端存储token
sessionStorage.setItem("adminToken",resp.data.data.token);
5、之后的每次请求,都将token携带着向后端发送
//axios 请求拦截 每发送一次http请求,都会执行此拦截器
axios.interceptors.request.use(config => {
//为请求头对象,添加 Token 验证的 token 字段
config.headers.adminToken = window.sessionStorage.getItem('adminToken');
return config;
})
6、后端对请求中的token进行解析验证
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("token验证的过滤器");
//接收请求头中的adminToken
HttpServletRequest request=(HttpServletRequest) servletRequest;
String adminToken=request.getHeader("adminToken");
boolean verify=JWTUtil.verify(adminToken);
if(verify){
filterChain.doFilter(servletRequest, servletResponse);//继续向后执行
}else{
//token验证失败
servletResponse.setContentType("text/html;charset=utf-8");
PrintWriter printWriter=servletResponse.getWriter();
CommonResult commonResult=new CommonResult(401, "token验证失败,请重新登录");
ObjectMapper objectMapper=new ObjectMapper();
String json=objectMapper.writeValueAsString(commonResult);
printWriter.print(json);
}
}