前端項目搭建參考:
Vue項目的搭建和啟動_vue項目啟動 csdn-CSDN博客
Vue + ElementUI 登錄頁面_vue用戶登錄頁面-CSDN博客
跨域問題前端解決-CSDN博客
實現思路:
1. 實現的目的:為了保護網站安全信息,使用jwt進行權限驗證,也就是說只有登錄用戶才可以看到網站的一些特定內容。那么瀏覽器每次發起請求時,就需要帶上攜帶加密登錄用戶信息的token令牌。而token是用戶登錄時后端返回給前端的。
2. 編寫登錄接口,傳入的登錄form表單跟數據庫儲存的數據一致時,接口返回給前端之前,生成一個token,跟著登錄信息一起返回
3. 前端拿到從服務端返回的token,儲存于localStorage中,然后在前端配置發起請求時統一的攔截器,在請求頭中塞入這個token,這樣每個請求就都攜帶了token
4. 后端這個時候就需要校驗了,你這個請求想要查看我網站的安全信息,我得先看看你有沒有相應用戶的權限,是否之前登錄過,這個依據就是是否攜帶了正確的token。后端需要對大多數請求都進行這個校驗,而每個請求的校驗邏輯是一樣的,所以后端需要寫一個攔截器,攔截所有的請求,只有檢驗你是帶著正確的token來的,才可以訪問資源,當然登錄接口需要放開。
5. 最后還需要在前端的接收請求攔截器中,當某個請求沒有權限,即返回code=401時,需要跳轉到登錄頁面引導用戶登錄或者注冊。
1. pom文件添加JWT依賴
<!--jwt-->
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>${jwt-version}</version>
</dependency>
2. 生成token,在登錄接口返回給前端
/*** 生成token* @param userId 賬號* @param sign 密碼* @return String*/public static String getToken(String userId, String sign) {return JWT.create().withAudience(userId) //將userId荷載.withExpiresAt(DateUtil.offsetHour(new Date(), 2)) //當前時間2小時后過期.sign(Algorithm.HMAC256(sign)); //password作為token的密鑰}
@RestController
public class LoginController {@Autowiredprivate UserService userService;@PostMapping("/login")public Result<JSONObject> login(@RequestBody User user) {Result<JSONObject> result = new Result<JSONObject>();if (StrUtil.isBlank(user.getUserName()) || StrUtil.isBlank(user.getPassword())) {return Result.error("數據輸入不合法");}User dbUser = userService.login(user);//生成tokenString token = TokenUtils.getToken(user.getUserName(), user.getPassword());JSONObject obj = new JSONObject();obj.set("user", dbUser);obj.set("token", token);result.setResult(obj);result.success("登錄成功");return result;}
}
?3. 前端將token存入localStorage,設置請求攔截器,給每個請求帶上token
submitForm() {this.$refs.form.validate((valid) => {if (valid) {let loginUrl = '/login'postAction(loginUrl, this.form).then((res) => {if (res.success) {let token = res.result.token;localStorage.setItem("token", token)this.$message.success("登錄成功!")router.push("/")}})}else {this.$message({message: '請輸入用戶名和密碼',type: 'warning'});}return false;})}
// request攔截器
// 可以在請求發送之前對請求做一些處理
// 比如統一加token,對請求參數統一加密
request.interceptors.request.use(config => {
// 可以在這里添加認證 tokenconfig.headers['Content-Type'] = 'application/json;charset=utf-8';// config.headers['Authorization'] = 'Bearer your-token';config.headers['token'] = localStorage.getItem("token");return config;},error => {return Promise.reject(error);}
);
??4. 后端設置jwt攔截器,并配置
/*** @Author: EstellaQ* @Date: 2025/4/7 9:20* @Description: jwt攔截器**/
public class JwtInterceptor implements HandlerInterceptor {@Resourceprivate UserMapper userMapper;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("token");if (StrUtil.isBlank(token)) {token = request.getParameter("token");}//如果不是映射到方法直接通過if (handler instanceof HandlerMethod) {//通過反射拿到方法上的注解AuthAccess annotation = ((HandlerMethod) handler).getMethodAnnotation(AuthAccess.class);if (annotation != null) {return true;}}//執行認證if (StrUtil.isBlank(token)) {throw new ServiceException(401, "請登錄");}//獲取token中的useridString userId;try {//解碼userId = JWT.decode(token).getAudience().get(0);} catch (JWTDecodeException e) {throw new ServiceException(401, "請登錄");}//根據token中的userid查詢數據庫User user = userMapper.selectByUserName(userId);if (user == null) {throw new ServiceException(401, "請登錄");}//用戶密碼加簽驗證 tokenJWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();try {jwtVerifier.verify(token); //驗證token} catch (JWTVerificationException e) {throw new ServiceException(401, "請登錄");}return true;}
}
/*** @Author: EstellaQ* @Date: 2025/4/7 9:54* @Description: 配置攔截規則**/
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {@Overrideprotected void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtInterceptor()).addPathPatterns("/**") //攔截所有的請求路徑.excludePathPatterns("/login"); //將登錄放行super.addInterceptors(registry);}@Beanpublic JwtInterceptor jwtInterceptor() {return new JwtInterceptor();}
}
??5. 前端設置接口接收之后的攔截器,若權限認證失敗,返回401,則返回登錄接口
request.interceptors.response.use(response => {
// 處理響應數據let res = response.data// 兼容服務端返回的字符串數據if (typeof res == 'string') {res = res ? JSON.parse(res) : res}if (res.code === 401) {router.push("/login")}return res},error => {
// 處理響應錯誤return Promise.reject(error);}
);