Springboot3+Vue3實(shí)現(xiàn)JWT登錄鑒權(quán)功能
做鑒權(quán)原因:
管理系統(tǒng)的數(shù)據(jù)是敏感的,隱私的,每個(gè)角色的權(quán)限是不同的,必須在數(shù)據(jù)的增刪改查操作時(shí)候?qū)υL問的用戶進(jìn)行權(quán)限驗(yàn)證
JWT(Json Web Token)
用于在網(wǎng)絡(luò)應(yīng)用間安全的傳遞消息。它以緊湊且自包含的方式,通過JSON對(duì)象在各方之間傳遞經(jīng)過驗(yàn)證的信息。JWT通常由三部分組成,用點(diǎn)號(hào)(.)分隔:header.payload.signature
集成JWT(在pom中引入依賴)
<!--java-JWT坐標(biāo) -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>生成token
/**
* 生成 JWT 令牌
*/
public static String createToken(String data,String sign) {
return JWT.create().withAudience(data)//將userid-role保存到token里面作為載荷
.withExpiresAt(DateUtil.offsetDay(new Date(),1))//1天后token過期
.sign(Algorithm.HMAC256(sign));//以password作為token的密鑰,使用HMAC256算法加密
}在***Service中創(chuàng)建token返回前端
String token = TokenUtil.createToken(dbUser.getId()+"-"+"管理員",dbUser.getPassword());<br data-filtered="filtered">dbUser.setToken(token);
Token格式

"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiIxLeeuoeeQhuWRmCIsImV4cCI6MTc0MjkyMTk5Mn0.PH2OJMzhqZFuJz-aW5nWfE5wZk9fbM-tgxPql1_NNVI"
JWT攔截器對(duì)所有訪問的接口進(jìn)行驗(yàn)證
通過webConfig做一層攔截器攔截所有的接口
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor())
.addPathPatterns("/**")//校驗(yàn)規(guī)則所有接口
.excludePathPatterns("/login","/register");//排除登錄和注冊(cè)接口
}
@Bean
public JWTInterceptor jwtInterceptor(){
return new JWTInterceptor();
}
}JWT攔截器
/**
* JWT攔截器
* 做攔截器的實(shí)現(xiàn)
* 對(duì)Token進(jìn)行攔截并進(jìn)一步解析Token、驗(yàn)證Token,看看Token是否是合法的
*/
@Component
public class JWTInterceptor implements HandlerInterceptor {
@Resource
private UserService userService;
@Resource
private ZuKeService zuKeService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1.從請(qǐng)求頭拿到Token
String token=request.getHeader("token");
if (StrUtil.isEmpty(token)){
//如果沒拿到,從參數(shù)中再拿一次
token=request.getParameter("token");
}
//2.認(rèn)證Token
if (StrUtil.isBlank(token)){
throw new CustomException("401","您無權(quán)操作");
}
Account account=null;
try {
//拿到Token載荷數(shù)據(jù)
String audience = JWT.decode(token).getAudience().get(0);
String[] split=audience.split("-");
String userId=split[0];
String role=split[1];
//柑橘Token解析出來的userId去對(duì)應(yīng)的表查詢信息
if ("管理員".equals(role)){
account=userService.selectById(userId);
} else if ("租客".equals(role)) {
account=zuKeService.selectById(userId);
}
} catch (Exception e) {
throw new CustomException("401","您無權(quán)操作");
}
if (account==null){
throw new CustomException("401","您無權(quán)操作");
}
try {
//用戶加簽 驗(yàn)證簽名
JWTVerifier jwtVerifier=JWT.require(Algorithm.HMAC256(account.getPassword())).build();
jwtVerifier.verify(token);
} catch (Exception e) {
throw new CustomException("401","您無權(quán)操作");
}
return true;
}
}出現(xiàn)401錯(cuò)誤,無權(quán)訪問數(shù)據(jù)怎么辦

在Vue的request.js的攔截器里面添加統(tǒng)一的請(qǐng)求頭Token


request.js的代碼
import axios from "axios";
import {ElMessage} from "element-plus";
const request = axios.create({
baseURL:'http://localhost:8080',//后端統(tǒng)一的請(qǐng)求地址
timeout:30000 //后臺(tái)接口時(shí)間
})
//request 攔截器
//可以自請(qǐng)求發(fā)送前對(duì)請(qǐng)求做一些處理
request.interceptors.request.use(config =>{
//統(tǒng)一的數(shù)據(jù)傳輸格式為json,統(tǒng)一的編碼utf-8
config.headers['Content-Type']='application/json;charset=utf-8';
//let user=JSON.parse(localStorage.getItem('pro1-user') || '{}');
//config.headers['token']=user.token;
// ? 安全獲取 user(兼容 null 和異常情況)
let user = {};
try {
const userStr = localStorage.getItem('pro1-user');
user = userStr ? JSON.parse(userStr) : {};
} catch (e) {
console.error('解析 pro1-user 失敗:', e);
}
// 僅當(dāng) token 存在時(shí)才添加到 headers
if (user.token) {
config.headers['token'] = user.token;
} else {
console.warn('Token 不存在,請(qǐng)求可能被后端拒絕');
// 可選:跳轉(zhuǎn)到登錄頁
// window.location.href = '/login';
}
return config;
},error=>{
return Promise.reject(error)
});
//response攔截器
//可以在接口響應(yīng)后統(tǒng)一處理結(jié)果
request.interceptors.response.use(
response =>{
let res=response.data;
//兼容服務(wù)端返回的字符串?dāng)?shù)據(jù)
if(typeof res === 'string'){
//如果是string,轉(zhuǎn)成json
res = res ? JSON.parse(res) : res
}
if (res.code === '401'){
ElMessage.error(res.msg);
router.push('/login')
}else {
return res;
}
},
error =>{
//后端返回?cái)?shù)據(jù)判斷
if (error.response.status === 404){
ElMessage.error('未找到請(qǐng)求接口')
}else if (error.response.status === 500){
ElMessage.error('系統(tǒng)異常,請(qǐng)查看后端控制臺(tái)報(bào)錯(cuò)')
}else{
console.error(error.message)
}
return Promise.reject(error)
}
)
export default request獲取當(dāng)前登錄用戶信息
@Component
public class TokenUtil {
@Resource
UserService userService;
@Resource
ZuKeService zuKeService;
static UserService stasticUserService;
static ZuKeService stasticZuKeService;
/**
* 生成 JWT 令牌
*/
public static String createToken(String data,String sign) {
return JWT.create().withAudience(data)//將userid-role保存到token里面作為載荷
.withExpiresAt(DateUtil.offsetDay(new Date(),1))//1天后token過期
.sign(Algorithm.HMAC256(sign));//以password作為token的密鑰,使用HMAC256算法加密
}
/**
* 獲取當(dāng)前登錄用戶信息
* @return
*/
public static Account getCurrentUser(){
Account account=null;
HttpServletRequest request=((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
String token=request.getHeader("token");
if (StrUtil.isEmpty(token)){
//如果沒拿到,從參數(shù)中再拿一次
token=request.getParameter("token");
}
//拿到Token載荷數(shù)據(jù)
String audience = JWT.decode(token).getAudience().get(0);
String[] split=audience.split("-");
String userId=split[0];
String role=split[1];
if ("管理員".equals(role)){
return stasticUserService.selectById(userId);
}else if ("租客".equals(role)){
return stasticZuKeService.selectById(userId);
}
return null;
}
}在service方法里面獲取當(dāng)前登錄用戶的信息
Account currentUser=TokenUtil.getCurrentUser();
到此這篇關(guān)于Springboot3+Vue3實(shí)現(xiàn)JWT登錄鑒權(quán)的文章就介紹到這了,更多相關(guān)Springboot3+Vue3實(shí)現(xiàn)JWT登錄鑒權(quán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 入門圖形用戶界面設(shè)計(jì)之列表框JList
圖形界面(簡(jiǎn)稱GUI)是指采用圖形方式顯示的計(jì)算機(jī)操作用戶界面。與早期計(jì)算機(jī)使用的命令行界面相比,圖形界面對(duì)于用戶來說在視覺上更易于接受,本篇精講Java語言中關(guān)于圖形用戶界面的列表框JList2022-02-02
在IntelliJ?IDEA中配置SSH服務(wù)器開發(fā)環(huán)境并實(shí)現(xiàn)固定地址遠(yuǎn)程連接的操作方法
本文主要介紹如何在IDEA中設(shè)置遠(yuǎn)程連接服務(wù)器開發(fā)環(huán)境,并結(jié)合Cpolar內(nèi)網(wǎng)穿透工具實(shí)現(xiàn)無公網(wǎng)遠(yuǎn)程連接,然后實(shí)現(xiàn)遠(yuǎn)程Linux環(huán)境進(jìn)行開發(fā),本例使用的是IDEA2023.2.5版本,感興趣的朋友跟隨小編一起看看吧2024-01-01
Spring之兩種任務(wù)調(diào)度Scheduled和Async詳解
這篇文章主要介紹了Spring之兩種任務(wù)調(diào)度Scheduled和Async,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10
手?jǐn)]一個(gè) spring-boot-starter的全過程
這篇文章主要介紹了手?jǐn)]一個(gè) spring-boot-starter的全過程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
Java中通過三級(jí)緩存解決Spring循環(huán)依賴詳解
這篇文章主要介紹了Java中通過三級(jí)緩存解決Spring循環(huán)依賴詳解,當(dāng)出現(xiàn)兩個(gè)或多個(gè) Bean 在初始化時(shí)相互依賴的情況時(shí),Spring Boot 會(huì)將其中一個(gè) Bean 提前暴露出來,以便其他 Bean 能夠在初始化時(shí)正確地引用它,這一策略能有效避免循環(huán)依賴導(dǎo)致的問題,需要的朋友可以參考下2023-09-09

