SpringBoot+vue實現(xiàn)token認證登錄過程
在Spring Boot和Vue.js中實現(xiàn)token認證登錄是一種常見的前后端分離的認證機制。
以下是實現(xiàn)這一機制的基本步驟:
后端(Spring Boot)
1. 創(chuàng)建用戶實體和數(shù)據(jù)庫表
首先,你需要定義一個用戶實體,比如User,并為其創(chuàng)建相應(yīng)的數(shù)據(jù)庫表。
@TableField(exist = false) private String token; // 表示我們數(shù)據(jù)庫沒有這個字段,但是在前端我們需要返回這個字段
2. 用戶注冊和登錄接口
在Spring Boot應(yīng)用中創(chuàng)建用于注冊和登錄的REST API。
@Override
public User login(UserPasswordDTO userPasswordDTO) {
User one = getUserInfo(userPasswordDTO);
if (one != null) {
BeanUtil.copyProperties(one, userPasswordDTO, true);
// 設(shè)置token
String token = TokenUtils.genToken(one.getId().toString(), one.getPassword());
one.setToken(token);
return one;
}else{
return null;
}
}
private User getUserInfo(UserPasswordDTO userPasswordDTO){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", userPasswordDTO.getUsername());
queryWrapper.eq("password", SecureUtil.md5(userPasswordDTO.getPassword()));
List<User> one = userMapper.selectList(queryWrapper);
if (one.size()==0){
return null;
}
return one.get(0);
}3. JWT Token生成
用戶登錄成功后,生成一個JWT(JSON Web Token),并將其發(fā)送回客戶端。
首先在pom.xml文件中導入jwt包:
<!-- JWT -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>在config包中新建TokenUtils類:
package com.lyk.xuelang.config;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.lyk.xuelang.entity.User;
import com.lyk.xuelang.mapper.UserMapper;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
@Component
public class TokenUtils {
private static UserMapper staticUserMapper;
@Resource
private UserMapper userMapper;
@PostConstruct
public void setUserService() {
staticUserMapper = userMapper;
}
/**
* 生成token
*
* @return
*/
public static String genToken(String userId, String sign) {
return JWT.create().withAudience(userId) // 將 user id 保存到 token 里面,作為載荷
.withExpiresAt(DateUtil.offsetHour(new Date(), 2)) // 2小時后token過期
.sign(Algorithm.HMAC256(sign)); // 以 password 作為 token 的密鑰
}
/**
* 獲取當前登錄的用戶信息
*
* @return user對象
*/
public static User getCurrentUser() {
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader("token");
if (StrUtil.isNotBlank(token)) {
String userId = JWT.decode(token).getAudience().get(0);
return staticUserMapper.selectById(Integer.valueOf(userId));
}
} catch (Exception e) {
return null;
}
return null;
}
}
4. JWT Token驗證
在config包下新建interceptor包,然后新建JwtInterceptor類:
package com.lyk.xuelang.config.interceptor;
import cn.hutool.core.util.StrUtil;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.lyk.xuelang.common.Constants;
import com.lyk.xuelang.entity.User;
import com.lyk.xuelang.exception.ServiceException;
import com.lyk.xuelang.service.IUserService;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class JwtInterceptor implements HandlerInterceptor {
@Resource
private IUserService userService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
if(!(handler instanceof HandlerMethod)){
return true;
}
// 執(zhí)行認證
if (StrUtil.isBlank(token)) {
throw new ServiceException(Constants.CODE_401, "無token驗證失敗");
}
// 獲取 token 中的 userId
String userId;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
String errMsg = "token驗證失敗,請重新登錄";
throw new ServiceException(Constants.CODE_401, errMsg);
}
// 根據(jù)token中的userid查詢數(shù)據(jù)庫
User user = userService.getById(userId);
if (user == null) {
throw new ServiceException(Constants.CODE_401, "用戶不存在,請重新登錄");
}
// 用戶密碼加簽驗證 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token); // 驗證token
} catch (JWTVerificationException e) {
throw new ServiceException(Constants.CODE_401, "token驗證失敗,請重新登錄");
}
return true;
}
}
添加自定義攔截器
在config包下新建InterceptorConfig類:
package com.lyk.xuelang.config;
import com.lyk.xuelang.config.interceptor.JwtInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
// 加自定義攔截器JwtInterceptor,設(shè)置攔截規(guī)則
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptorl())
.addPathPatterns("/**") //攔截所有請求,通過判斷token是否合法來決定是否登錄
.excludePathPatterns("/login","/role/page","/**/export","/**/import");//排除這些接口,也就是說,這些接口可以放行
}
@Bean
public JwtInterceptor jwtInterceptorl(){
return new JwtInterceptor();
}
}
前端(Vue.js)
1. 用戶界面
創(chuàng)建登錄表單,允許用戶輸入用戶名和密碼。
<div class="login-container">
<el-card class="login-card">
<h2 class="login-title">倉庫管理系統(tǒng)</h2>
<el-form :model="loginForm" :rules="rules" ref="loginForm" label-width="20">
<el-form-item label="用戶名" prop="username">
<el-input v-model="loginForm.username" placeholder="請輸入用戶名"></el-input>
</el-form-item>
<el-form-item label="密碼" prop="password">
<el-input type="password" v-model="loginForm.password" placeholder="請輸入密碼"></el-input>
</el-form-item>
<el-form-item style="margin: 10px 0;text-align: right;" label="溫馨提示:忘記密碼?聯(lián)系管理員!">
<el-button type="primary" autocomplete="off" @click="submitForm">登錄</el-button>
</el-form-item>
</el-form>
</el-card>
</div>2. 發(fā)送登錄請求
用戶提交表單后,前端發(fā)送登錄請求到后端的登錄接口。
login.js代碼如下:
import request from '@/utils/request'
// 用戶登錄
export function login (user) {
return request({
url: '/login',
method: 'post',
data: user
})
}Home.vue頁面代碼如下:
<script>
import { login } from '@/api/login'
export default {
data () {
return {
loginForm: {
username: '',
password: ''
},
rules: {
username: [
{ required: true, message: '請輸入用戶名!', trigger: 'blur' }
],
password: [
{ required: true, message: '請輸入密碼!', trigger: 'blur' }
]
}
}
},
methods: {
submitForm () {
this.$refs.loginForm.validate(async (valid) => {
if (valid) {
// 在這里添加你的登錄邏輯
const res = await login(this.loginForm)
if (res.code === 200) {
this.$message.success('登錄成功')
localStorage.setItem('user', JSON.stringify(res.data)) // 存儲用戶信息到瀏覽器
localStorage.setItem('token', JSON.stringify(res.data.token))
this.$router.push('/main')
} else {
this.$message.error(res.msg)
}
} else {
this.$message.error('賬號密碼錯誤,請重新輸入!')
return false
}
})
}
}
}
</script>3. 接收并存儲Token
登錄成功后,前端接收JWT Token,并將其存儲在本地存儲(localStorage)或Vuex狀態(tài)管理中。
// 在這里添加你的登錄邏輯
const res = await login(this.loginForm)
if (res.code === 200) {
this.$message.success('登錄成功')
localStorage.setItem('user', JSON.stringify(res.data)) // 存儲用戶信息到瀏覽器
localStorage.setItem('token', JSON.stringify(res.data.token))
this.$router.push('/main')
}4. 發(fā)送請求時攜帶Token
在發(fā)送需要認證的請求時,前端需要在請求頭中攜帶JWT Token。
// 添加請求攔截器,一下內(nèi)容是axios的攔截器,可以不用寫
request.interceptors.request.use(config => {
config.headers['Content-Type'] = 'application/json;charset=utf-8'
const user = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user')) : null
if (user) {
config.headers.token = user.token
}
return config
}, error => {
return Promise.reject(error)
})5. 路由守衛(wèi)
使用Vue Router的路由守衛(wèi)來保護需要認證的路由。
{ path: '/dashboard',
component: Dashboard,
meta:
{requiresAuth: true}
} // 標記需要驗證的路由router.beforeEach((to, from, next) => {
const auth = require('@/router/auth').default // 引入認證守衛(wèi)
auth.redirectIfNotAuthenticated(to, from, next)
})auth.js代碼如下:
export default {
isAuthenticated () {
// 這里應(yīng)該根據(jù)你的應(yīng)用邏輯來檢查用戶是否登錄
// 例如,檢查本地存儲(localStorage)中是否有token
return localStorage.getItem('token') !== null
},
redirectIfNotAuthenticated (to, from, next) {
if (!this.isAuthenticated()) {
to.path !== '/login' && to.matched.some(record => record.meta.requiresAuth) ? next({ path: '/login' }) : next()
} else {
next()
}
}
}
注意事項
- 安全性:確保使用HTTPS來傳輸JWT Token。
- Token存儲:考慮使用HttpOnly的Cookie來存儲Token,以避免XSS攻擊。
- Token過期:JWT Token應(yīng)該有過期時間,并且后端需要處理Token的刷新。
這只是一個簡單的示例,實際應(yīng)用中可能需要更復雜的邏輯,比如密碼加密、Token刷新機制、用戶角色和權(quán)限管理等。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
springboot Interceptor攔截器excludePathPatterns忽略失效
這篇文章主要介紹了springboot Interceptor攔截器excludePathPatterns忽略失效的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
在SpringBoot項目中實現(xiàn)讀寫分離的流程步驟
SpringBoot作為一種快速開發(fā)框架,廣泛應(yīng)用于Java項目中,在一些大型應(yīng)用中,數(shù)據(jù)庫的讀寫分離是提升性能和擴展性的一種重要手段,本文將介紹如何在SpringBoot項目中優(yōu)雅地實現(xiàn)讀寫分離,并通過適當?shù)拇a插入,詳細展開實現(xiàn)步驟,同時進行拓展和分析2023-11-11
Netty網(wǎng)絡(luò)編程實戰(zhàn)之開發(fā)聊天室功能
這篇文章主要為大家詳細介紹了如何利用Netty實現(xiàn)聊天室功能,文中的示例代碼講解詳細,對我們學習Netty網(wǎng)絡(luò)編程有一定幫助,需要的可以參考一下2022-10-10
IDEA如何自動生成serialVersionUID的設(shè)置
這篇文章主要介紹了IDEA如何自動生成 serialVersionUID 的設(shè)置,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-09-09
SpringBoot?實現(xiàn)CAS?Server統(tǒng)一登錄認證的詳細步驟
??CAS(Central?Authentication?Service)中心授權(quán)服務(wù),是一個開源項目,目的在于為Web應(yīng)用系統(tǒng)提供一種可靠的單點登錄,這篇文章主要介紹了SpringBoot?實現(xiàn)CAS?Server統(tǒng)一登錄認證,需要的朋友可以參考下2024-02-02
Linux下Java開發(fā)環(huán)境搭建以及第一個HelloWorld
這篇文章主要介紹了Linux下Java開發(fā)環(huán)境搭建以及第一個HelloWorld的實現(xiàn)過程,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2015-09-09
五分鐘教你手寫 SpringBoot 本地事務(wù)管理實現(xiàn)
這篇文章主要介紹了五分鐘教你手寫 SpringBoot 本地事務(wù)管理實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-02-02

