javascript
springboot entity date_SpringBoot+JWT实战(附源码)
SpringBoot集成JWT
首先我們搭建好SpringBoot框架,SpringBoot環(huán)境準(zhǔn)備就緒。接下來執(zhí)行以下操作:
1.引入依賴
引入JWT依賴,由于是基于Java,所以需要的是java-jwt。
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.5.0</version> </dependency>2.自定義注解
在這一步,我們?cè)赼nnotation包下定義一個(gè)用戶需要登錄才能進(jìn)行其他接口訪問等一系列操作的注解TokenRequired。
`@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface TokenRequired {boolean required() default true; }@Target旨意為我們自定義注解@TokenRequired的作用目標(biāo),因?yàn)槲覀儽敬巫⒔獾淖饔媚繕?biāo)為方法層級(jí),因此使用 ElementType.METHOD。
@Retention旨意為我們自定義注解 @TokenRequired的保留位置,@TokenRequired的保留位置被定義為RetentionPolicy.RUNTIME這種類型的注解將被JVM保留,他能在運(yùn)行時(shí)被JVM或其他使用反射機(jī)制的代碼所讀取和使用。
3.定義實(shí)體類
在entity包中,我們使用lombok,簡(jiǎn)單自定義一個(gè)實(shí)體類User。
`@Data @AllArgsConstructor @NoArgsConstructor public class User {String Id;String username;String password; }4.定義一個(gè)JWT工具類
在這一步,我們?cè)趗til包下面創(chuàng)建一個(gè)JwtUtil工具類,用于生成token和校驗(yàn)token。
`public class JwtUtil {//過期時(shí)間15分鐘private static final long EXPIRE_TIME = 15*60*1000;//生成簽名,15分鐘后過期public static String sign(String username,String userId,String password){//過期時(shí)間Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);//使用用戶密碼作為私鑰進(jìn)行加密Algorithm algorithm = Algorithm.HMAC256(password);//設(shè)置頭信息HashMap<String, Object> header = new HashMap<>(2);header.put("typ", "JWT");header.put("alg", "HS256");//附帶username和userID生成簽名return JWT.create().withHeader(header).withClaim("userId",userId).withClaim("username",username).withExpiresAt(date).sign(algorithm);}//校驗(yàn)tokenpublic static boolean verity(String token,String password){try {Algorithm algorithm = Algorithm.HMAC256(password);JWTVerifier verifier = JWT.require(algorithm).build();verifier.verify(token);return true;} catch (IllegalArgumentException e) {return false;} catch (JWTVerificationException e) {return false;}} }5.業(yè)務(wù)校驗(yàn)并生成token
在service包下,我們創(chuàng)建一個(gè)UserService,并定義一個(gè)login方法,用于做登錄接口的業(yè)務(wù)層數(shù)據(jù)校驗(yàn),并調(diào)取JwtUtil中方法生成token。
`@Service("UserService") public class UserService {@AutowiredUserMapper userMapper;public String login(String name, String password) {String token = null;try {//校驗(yàn)用戶是否存在User user = userMapper.findByUsername(name);if(user == null){ResultDTO.failure(new ResultError(UserError.EMP_IS_NULL_EXIT));}else{//檢驗(yàn)用戶密碼是否正確if(!user.getPassword().equals(password)){ResultDTO.failure(new ResultError(UserError.PASSWORD_OR_NAME_IS_ERROR));}else {// 生成token,將 user id 、userName保存到 token 里面token = JwtUtil.sign(user.getUsername(),user.getId(),user.getPassword());}}} catch (Exception e) {e.printStackTrace();}return token;} }Algorithm.HMAC256():使用HS256生成token,密鑰則是用戶的密碼,唯一密鑰的話可以保存在服務(wù)端。withAudience()存入需要保存在token的信息,這里我把用戶ID存入token中。
6.定義攔截器
接下來我們需要寫一個(gè)攔截器去獲取token并驗(yàn)證token。
`public class AuthenticationInterceptor implements HandlerInterceptor {@AutowiredUserService userService;@Overridepublic boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {// 從 http 請(qǐng)求頭中取出 tokenString token = httpServletRequest.getHeader("token");// 如果不是映射到方法直接通過if(!(object instanceof HandlerMethod)){return true;}HandlerMethod handlerMethod=(HandlerMethod)object;Method method=handlerMethod.getMethod();//檢查有沒有需要用戶權(quán)限的注解if (method.isAnnotationPresent(TokenRequired.class)) {TokenRequired userLoginToken = method.getAnnotation(TokenRequired.class);if (userLoginToken.required()) {// 執(zhí)行認(rèn)證if (token == null) {throw new RuntimeException("無token,請(qǐng)重新登錄");}// 獲取 token 中的 user idString userId;try {userId = JWT.decode(token).getClaim("userId").asString();} catch (JWTDecodeException j) {throw new RuntimeException("401");}User user = userService.findUserById(userId);if (user == null) {throw new RuntimeException("用戶不存在,請(qǐng)重新登錄");}// 驗(yàn)證 tokentry {if(!JwtUtil.verity(token,user.getPassword())){throw new RuntimeException("無效的令牌");}} catch (JWTVerificationException e) {throw new RuntimeException("401");}return true;}}return true;}@Overridepublic void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {} }AuthenticationInterceptor攔截器實(shí)現(xiàn)了HandlerInterceptor接口的三個(gè)方法:
預(yù)處理回調(diào)方法,實(shí)現(xiàn)處理器的預(yù)處理,第三個(gè)參數(shù)為響應(yīng)的處理器,自定義Controller返回值,返回值為true會(huì)調(diào)用下一個(gè)攔截器或處理器,或者接著執(zhí)行postHandle()和afterCompletion();false表示流程中斷,不會(huì)繼續(xù)調(diào)用其他的攔截器或處理器,中斷執(zhí)行。
2.void postHandle():
后處理回調(diào)方法,實(shí)現(xiàn)處理器的后處理(DispatcherServlet進(jìn)行視圖返回渲染之前進(jìn)行調(diào)用),此時(shí)我們可以通過modelAndView對(duì)模型數(shù)據(jù)進(jìn)行處理或?qū)σ晥D進(jìn)行處理,modelAndView也可能為null。
3.void afterCompletion():
整個(gè)請(qǐng)求處理完畢回調(diào)方法,該方法也是需要當(dāng)前對(duì)應(yīng)的Interceptor的preHandle()的返回值為true時(shí)才會(huì)執(zhí)行,也就是在DispatcherServlet渲染了對(duì)應(yīng)的視圖之后執(zhí)行。用于進(jìn)行資源清理。
該攔截器的執(zhí)行流程為:
7.配置攔截器
在配置類上添加了注解@Configuration,標(biāo)明了該類是一個(gè)配置類并且會(huì)將該類作為一個(gè)SpringBean添加到IOC容器內(nèi)。
`@Configuration public class InterceptorConfig implements WebMvcConfigurer {@Beanpublic AuthenticationInterceptor authenticationInterceptor() {return new AuthenticationInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 將我們上步定義的實(shí)現(xiàn)了HandlerInterceptor接口的攔截器實(shí)例authenticationInterceptor添加InterceptorRegistration中,并設(shè)置過濾規(guī)則,所有請(qǐng)求都要經(jīng)過authenticationInterceptor攔截。registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**");} }WebMvcConfigurer接口是Spring內(nèi)部的一種配置方式,采用JavaBean的形式來代替?zhèn)鹘y(tǒng)的xml配置文件來實(shí)現(xiàn)基本的配置需要。
InterceptorConfig內(nèi)的addInterceptor需要一個(gè)實(shí)現(xiàn)HandlerInterceptor接口的攔截器實(shí)例,addPathPatterns方法用于設(shè)置攔截器的過濾路徑規(guī)則。
在addInterceptors方法中,我們將第6步定義的實(shí)現(xiàn)了HandlerInterceptor接口的攔截器實(shí)例authenticationInterceptor,添加至InterceptorRegistration中,并設(shè)置過濾路徑。現(xiàn)在,我們所有請(qǐng)求都要經(jīng)過authenticationInterceptor的攔截,攔截器authenticationInterceptor通過preHandle方法的業(yè)務(wù)過濾,判斷是否有@TokenRequired 來決定是否需要登錄。
8.定義接口方法并添加注解
`@RestController @RequestMapping("user") public class UserController {@AutowiredUserService userService;/*** 用戶登錄* @param user* @return*/@PostMapping("/login")public ResultDTO login( User user){String token = userService.login(user.getUsername(), user.getPassword());if (token == null) {return ResultDTO.failure(new ResultError(UserError.PASSWORD_OR_NAME_IS_ERROR));}Map<String, String> tokenMap = new HashMap<>();tokenMap.put("token", token);return ResultDTO.success(tokenMap);}@TokenRequired@GetMapping("/hello")public String getMessage(){return "你好哇,我是小碼仔";} } 不加注解的話默認(rèn)不驗(yàn)證,登錄接口一般是不驗(yàn)證的。所以我在getMessage()中加上了登錄注解,說明該接口必須登錄獲取token后,在請(qǐng)求頭中加上token并通過驗(yàn)證才可以訪問。請(qǐng)求驗(yàn)證
我在代碼中對(duì)getMessage()添加了@TokenRequired注解,此刻訪問該方法時(shí)必須要通過登錄拿取到token值,并在請(qǐng)求頭中添加token才可以訪問。我們現(xiàn)在做以下校驗(yàn):
如上圖所示,請(qǐng)求結(jié)果顯示:無token,請(qǐng)重新登錄
2.訪問登錄接口,獲取token,并在請(qǐng)求頭中添加token信息:
此時(shí),訪問成功
3.15分鐘后,token失效,我們?cè)俅卧谡?qǐng)求頭中添加token信息訪問:
此時(shí)token已失效,返回:無效的令牌。總結(jié)
回顧一下本次JWT使用的基本業(yè)務(wù)判斷流程:
不足之處:本次集成只是做一個(gè)簡(jiǎn)單的JWT使用介紹,沒有實(shí)現(xiàn)token的過期刷新機(jī)制,此種情況下用戶每隔15分鐘就需要重新登錄一次,如果在實(shí)際生產(chǎn)環(huán)境中使用,可能會(huì)被用戶打死,因此實(shí)際開發(fā)中并不推薦。
關(guān)于token的刷新機(jī)制,將在下篇文章中為大家解讀并附上源碼。
本次集成代碼地址:https://github.com/bailele1995/springboot-jjwt.git來源:https://juejin.im/post/5ea27c5be51d4546c27bdf94?utm_source=tuicool&utm_medium=referral
總結(jié)
以上是生活随笔為你收集整理的springboot entity date_SpringBoot+JWT实战(附源码)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 返回空格_Python面试
- 下一篇: springmvc是什么_当一个http