Spring Boot + JWT + Vue 实现前后端分离登录认证
参考 B 站楠哥教你学 Java的30 分钟学会 Spring Boot + JWT + Vue视频
JWT 介绍
什么是 JWT?
JSON Web Token,通过数字签名的方式,以 JSON 对象为载体,在不同的服务终端之间安全的传输信息。
JWT 有什么用?
JWT 最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含 JWT,系统在每次处理用户请求的之前,都要先进行 JWT 安全校验,通过之后再进行处理。
JWT 的组成
JWT 由 3 部分组成,用.拼接
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IlRvbSIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE2MjMyMjM2NzUsImp0aSI6ImQ2MTJjZjcxLWI5ZmUtNGMwNy04MzQwLTViOWViZmMyNjExNyJ9.FOS9Y7rYNdc2AOidnSPrgg2XTYePU0yGZ598h2gtabE |
这三部分分别是:
Header
1 | { |
Payload
1 | { |
Signature
1 | var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); |
所需依赖
pom.xml
1 | <dependency> |
实现登录认证功能(核心代码)
本次代码以用户 User 为例
前端(Vue 3.x)
在
Login.vue
里添加代码:1
2
3
4
5
6
7
8
9
10
11
12login() {
request.post("/user/login", this.form).then((res) => {
if (res.code === "0") {
localStorage.setItem('access', JSON.stringify(res.data))
let message = '欢迎"' + res.data.username + '"用户登录'
ElMessage.success(message);
this.$router.replace('/'); //登录成功之后进行页面的跳转,跳转到首页
} else {
ElMessage.error(res.msg);
}
});
},在
src\router\index.js
添加代码:(按需更改)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52// 路由跳转之前要进行的操作
router.beforeEach((to, from, next) => {
//如果当前在登录页面,则把本地存储用户信息移除
if (to.path == "/login" || to.path == "/register") {
window.localStorage.removeItem("access");
next();
} else {
//如果在其它页面,则将用户信息在数据库里验证一下
let access = JSON.parse(window.localStorage.getItem("access"));
if (!access) {
ElMessage.warning("请先登录!");
next({ path: "/login" });
} else if (access.role == 1) {
//普通用户
//是否在后台页面
if (to.path.startsWith("/back")) {
next({ path: "/403" });
} else {
//校验token合法性
request
.get("/user/checkToken", {
headers: {
token: access.token,
},
})
.then((res) => {
if (!res) {
ElMessage.error("用户登录信息失效");
next({ path: "/login" });
}
next();
});
}
} else if (access.role == 2) {
//管理员
//校验token合法性
request
.get("/admin/checkToken", {
headers: {
token: access.token,
},
})
.then((res) => {
if (!res) {
ElMessage.error("管理员登录信息失效");
next({ path: "/login" });
}
next();
});
}
}
});添加
src\utils\request.js
:(按需添加)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44import axios from "axios";
const request = axios.create({
baseURL: "/api", // 注意!! 这里是全局统一加上了 '/api' 前缀,也就是说所有接口都会加上'/api'前缀在,页面里面写接口的时候就不要加 '/api'了,否则会出现2个'/api',类似 '/api/api/user'这样的报错,切记!!!
timeout: 5000,
});
// request 拦截器
// 可以自请求发送前对请求做一些处理
// 比如统一加token,对请求参数统一加密
request.interceptors.request.use(
(config) => {
config.headers["Content-Type"] = "application/json;charset=utf-8";
// config.headers['token'] = user.token; // 设置请求头
return config;
},
(error) => {
return Promise.reject(error);
}
);
// response 拦截器
// 可以在接口响应后统一处理结果
request.interceptors.response.use(
(response) => {
let res = response.data;
// 如果是返回的文件
if (response.config.responseType === "blob") {
return res;
}
// 兼容服务端返回的字符串数据
if (typeof res === "string") {
res = res ? JSON.parse(res) : res;
}
return res;
},
(error) => {
console.log("err" + error); // for debug
return Promise.reject(error);
}
);
export default request;
在前端解决跨域问题:
更改
vue.config.js
代码:(按需添加)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// 跨域配置
module.exports = {
devServer: {
port: 8060, // 启动端口号
proxy: {
//设置代理,必须填
"/api": {
//设置拦截器 拦截器格式 斜杠+拦截器名字,名字可以自己定
target: "http://localhost:6666", //代理的目标地址(后端)
changeOrigin: true, //是否设置同源,输入是的
pathRewrite: {
//路径重写
"/api": "", //选择忽略拦截器里面的单词
},
},
},
},
};
后端(Spring Boot)
src/main/java/com.hassan/controller/User.java
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//与数据库表名一一对应,必须写
public class User {
private Integer id;
private String username;
private String pwd;
private String nickname;
private String phone;
private String email;
private String token;
//普通用户为1,管理员为2
private int role = 1;
}新建
src/main/java/com.hassan/util/JwtUtil.java
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35public class JwtUtil {
private static long time = 1000*60*60*24; //有效期
private static String signature = "admin"; //签名
public static String createToken() {
JwtBuilder jwtBuilder = Jwts.builder();
String jwtToken = jwtBuilder
//header
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
//payload
.claim("username", "admin")
.claim("role", "admin")
.setSubject("admin-test")
.setExpiration(new Date(System.currentTimeMillis()+time))
.setId(UUID.randomUUID().toString())
//signature
.signWith(SignatureAlgorithm.HS256, signature)
.compact();
return jwtToken;
}
public static boolean checkToken(String token) {
if(token == null) {
return false;
}
try {
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
} catch (Exception e) {
return false;
}
return true;
}
}src/main/java/com.hassan/controller/UserController.java
:
1 | public class UserController { |
新建
src/main/java/com.hassan/common/Result.java
文件(按需添加)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58//Json数据
public class Result<T> {
private String code;
private String msg;
private T data;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Result() {
}
public Result(T data) {
this.data = data;
}
public static Result success() {
Result result = new Result<>();
result.setCode("0");
result.setMsg("成功");
return result;
}
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>(data);
result.setCode("0");
result.setMsg("成功");
return result;
}
public static Result error(String code, String msg) {
Result result = new Result();
result.setCode(code);
result.setMsg(msg);
return result;
}
}
补充
如何取出 localStorage 里的值?
比如:this.user = JSON.parse(window.localStorage.getItem('access'))
结束语:
哎,东拼西凑地把毕设上的登录认证功能给完成了,我太拉了
评论
隐私政策