怎么做校園表白墻網(wǎng)站怎么在網(wǎng)上做網(wǎng)絡(luò)營(yíng)銷
為實(shí)現(xiàn)Jwt簡(jiǎn)單的權(quán)限管理,我們需要用Jwt工具來(lái)生成token,也需要用Jwt來(lái)解碼token,同時(shí)需要添加Jwt攔截器來(lái)決定放行還是攔截。下面來(lái)實(shí)現(xiàn):
1、gradle引入Jwt、hutool插件
implementation 'com.auth0:java-jwt:3.10.3'implementation 'cn.hutool:hutool-all:5.3.7'
2、Jwt工具類,提供靜態(tài)方法生成token,和根據(jù)請(qǐng)求攜帶的token查找user信息
package com.zzz.simple_blog_backend.utils;import ......@Component
public class JwtTokenUtils {@Autowiredprivate UserService userService;private static UserService userServiceStatic;@PostConstruct//在spring容器初始化后執(zhí)行該方法public void setUserService() {userServiceStatic = userService;}//生成Tokenpublic static String genToken(String userId,String passwordSign) {return JWT.create().withAudience(userId)//放入載荷.withExpiresAt(DateUtil.offsetHour(new Date(), 2))//2小時(shí)后過(guò)期.sign(Algorithm.HMAC256(passwordSign));//密碼簽名作為密鑰}//通過(guò)token獲取當(dāng)前登錄用戶信息public static User getCurrentUser() {String token = null;HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();//1、獲取tokentoken = request.getHeader("token");if (StrUtil.isBlank(token)) {token = request.getParameter("token");}if (StrUtil.isBlank(token)) {throw new RuntimeException("沒(méi)有token,請(qǐng)重新登錄");}String userId;User user;try {userId = JWT.decode(token).getAudience().get(0);} catch (Exception e) {throw new RuntimeException("token驗(yàn)證失敗,請(qǐng)重新登錄!!!");}user = userServiceStatic.findById(Integer.parseInt(userId));if(user==null) {throw new RuntimeException("用戶id不存在,請(qǐng)重新登錄!!!");}//3、用密碼簽名,解碼判斷tokentry {JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();jwtVerifier.verify(token);} catch (JWTVerificationException e) {throw new CustomException(001, "token驗(yàn)證失敗,請(qǐng)重新登錄!!!");}return user;}
}
3、Jwt攔截器
SpringBoot添加攔截器,excludePathPatterns
可以指定不攔截的頁(yè)面,RestController
指定了請(qǐng)求前綴,控制器類要用@RestController
代替@Controller
,addInterceptor
添加了jwtInterceptor
攔截器。
package com.zzz.simple_blog_backend.config;import ...@Configuration
public class WebConfig implements WebMvcConfigurer{@Autowiredprivate JwtInterceptor jwtInterceptor;@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {//指定restcontroller統(tǒng)一接口前綴configurer.addPathPrefix("/api", clazz -> clazz.isAnnotationPresent(RestController.class));}@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 加自定義攔截器 給特定請(qǐng)求放行registry.addInterceptor(jwtInterceptor).addPathPatterns("/api/**").excludePathPatterns("/api/user/login","/api/user/register");}
}
仔細(xì)的童靴會(huì)發(fā)現(xiàn)JwtTokenUtils.getCurrentUser()
方法和JwtInterceptor
的攔截方法很像,主要區(qū)別就在if(user.getRole()!=0)
的判斷。所以JwtInterceptor
只會(huì)給管理員放行,如果需要給普通用戶放行而未登錄用戶不放行,那請(qǐng)求路徑先添加到excludePathPatterns
,并且控制類對(duì)應(yīng)的響應(yīng)方法第一句就先調(diào)用JwtTokenUtils.getCurrentUser()
判斷是否用戶已登錄。
package com.zzz.simple_blog_backend.config;import ......@Component
public class JwtInterceptor implements HandlerInterceptor{@Autowiredprivate UserService userService;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {//1、獲取tokenString token = request.getHeader("token");if (StrUtil.isBlank(token)) {token = request.getParameter("token");}if (StrUtil.isBlank(token)) {throw new RuntimeException("沒(méi)有token,請(qǐng)重新登錄");}//2、開(kāi)始認(rèn)證 解碼token,獲得userIdString userId;User user;try {userId = JWT.decode(token).getAudience().get(0);} catch (Exception e) {throw new RuntimeException("token驗(yàn)證失敗,請(qǐng)重新登錄!!!");}user = userService.findById(Integer.parseInt(userId));if(user==null) {throw new RuntimeException("用戶id不存在,請(qǐng)重新登錄!!!");}if(user.getRole()!=0) {throw new RuntimeException("非管理員賬號(hào),無(wú)權(quán)訪問(wèn)!!!");}//3、用密碼簽名,解碼判斷tokentry {JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();jwtVerifier.verify(token);} catch (JWTVerificationException e) {throw new RuntimeException("token驗(yàn)證失敗,請(qǐng)重新登錄!!!");}//token驗(yàn)證成功,放行return true;
// return HandlerInterceptor.super.preHandle(request, response, handler);}
}
4、前后端登錄操作
登錄后 后端返回帶token不帶密碼的user數(shù)據(jù)
@PostMapping("user/login")@ResponseBodypublic CommonResult<Object> login(@RequestBody User user){user = userService.findByUsernameAndPassword(user);if (user == null) {return CommonResult.failed(001, Message.createMessage("用戶名或密碼錯(cuò)誤!!!"));} else {//生成用戶對(duì)應(yīng)的TokenString token = JwtTokenUtils.genToken(user.getId().toString(), user.getPassword());user.setToken(token);//不傳輸密碼user.setPassword("");return CommonResult.success(user);}}
前端保存帶token的user
//axios的post請(qǐng)求成功后操作localStorage.setItem("user",JSON.stringify(response.data.data));//保存用戶信息
5、前端每次請(qǐng)求都攜帶token信息
假設(shè)已安裝axios,Axios.interceptors.request
攔截用戶所有請(qǐng)求,添加token信息后再發(fā)送請(qǐng)求,這樣后端就可以判斷了。
import Axios from 'axios'Vue.prototype.$http = Axios;
//添加向后端發(fā)起請(qǐng)求的服務(wù)器地址前綴
Axios.defaults.baseURL=AIOS_BASE_URL; // "http://127.0.0.1/api"
//設(shè)置請(qǐng)求超時(shí)時(shí)間
Axios.defaults.timeout=5000;//axios攔截器
//對(duì)接口request攔截
Axios.interceptors.request.use(function(config){//發(fā)起增刪改查請(qǐng)求時(shí),帶上token認(rèn)證var user = localStorage.getItem("user");if(user){config.headers["token"] = JSON.parse(user).token;}return config;
})
//攜帶證書(shū) session id 跨域請(qǐng)求的話需要
Axios.defaults.withCredentials = true
總結(jié)
Jwt實(shí)現(xiàn)權(quán)限管理的原理是登錄成功后 后端端生成token密鑰,隨著用戶信息發(fā)送給客戶端,客戶端接受并保存信息到本地localStorage。以后每次需要權(quán)限驗(yàn)證時(shí),根據(jù)客戶端返回的攜帶token信息,后端進(jìn)行攔截并解碼校驗(yàn),通過(guò)則放行,否則拋異常。如果要拋異常時(shí),返回錯(cuò)誤信息給前端,請(qǐng)看鏈接。