SpringBoot整合JWT實戰(zhàn)教程
SpringBoot整合JWT實戰(zhàn)詳解
jwt 介紹就不多說了,下面通過代碼演示開發(fā)過程中jwt 的使用。
(1)在pom.xml中引入對應的jar
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.7.0</version> </dependency>
(2)引入jwt 工具類:token的生成以及獲取對應的token信息
/** * @author : wl * @Description : * @date : 2020/7/3 13:25 */ public class JwtUtil { public static final String AUTHORIZATION_SECRET = "wlcoder"; private static final String UID = "uid"; private static final String USERNAME = "username"; private static final String PASSWORD = "password"; private static final String STATUS = "status"; //創(chuàng)建秘鑰 public static Key getKeyInstance() { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; byte[] bytes = DatatypeConverter.parseBase64Binary(AUTHORIZATION_SECRET); return new SecretKeySpec(bytes, signatureAlgorithm.getJcaName()); } /** * 生成token的方法 * * @param user * @param expire * @return */ public static String generatorToken(SysUser user, int expire) { return Jwts.builder().claim(UID, user.getId()) .claim(USERNAME, user.getUsername()) .claim(PASSWORD, user.getPassword()) .claim(STATUS, user.getStatus()) .setExpiration(DateTime.now().plusSeconds(expire).toDate()) .signWith(SignatureAlgorithm.HS256, getKeyInstance()) .compact(); } /** * 根據(jù)token獲取token中的信息 * * @param token * @return */ public static SysUser getTokenInfo(String token) { Jws<Claims> claimsJws = Jwts.parser().setSigningKey(getKeyInstance()).parseClaimsJws(token); Claims claims = claimsJws.getBody(); return SysUser.builder().id((Integer) claims.get(UID)) .username((String) claims.get(USERNAME)) .password((String) claims.get(PASSWORD)) .status((Integer) claims.get(STATUS)) .build(); } }
(3)添加注解 @NeedToken,@SkipToken ,加在方法上靈活處理對應的請求
/** * @author : wl * @Description : 需要token 驗證 * @date : 2020/7/3 11:40 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface NeedToken { boolean required() default true; }
/** * @author : wl * @Description :跳過token 驗證 * @date : 2020/7/3 11:39 */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface SkipToken { boolean required() default true; }
(4)添加攔截器,驗證前端請求是否需要token
/** * @author : wl * @Description :方法請求攔截 * @date : 2020/7/3 11:47 */ @Slf4j public class AuthenticationInterceptor implements HandlerInterceptor { @Autowired private SysUserService userService; @Autowired private RedisUtil redisUtil; @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws ServletException, IOException { String token = httpServletRequest.getHeader("token"); if (!(object instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) object; Method method = handlerMethod.getMethod(); //檢查有沒有跳過token的注解 if (method.isAnnotationPresent(SkipToken.class)) { SkipToken skipToken = method.getAnnotation(SkipToken.class); if (skipToken.required()) { log.info("該請求無須token驗證。。。"); return true; } } //檢查有沒有需要token的注解 if (method.isAnnotationPresent(NeedToken.class)) { NeedToken needToken = method.getAnnotation(NeedToken.class); if (needToken.required()) { log.info("該請求需要token驗證。。。"); if (Objects.isNull(token)) { throw new BaseException("無token,請重新登錄"); } try { JwtUtil.getTokenInfo(token); } catch (ExpiredJwtException e) { throw new BaseException("token超時"); } // SysUser user = userService.findUser(sysUser.getUsername(), sysUser.getPassword()); // if (Objects.isNull(user)) { // throw new BaseException("用戶不存在,請重新登錄"); // } if (!Objects.equals(token, redisUtil.get("ms_notify_token"))) { throw new BaseException("token異常,請重新登錄"); } } } return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
(5)添加攔截器異常處理 :出現(xiàn)異常直接跳轉到登錄頁面 ,這里有個坑,前端為ajax請求時候 使用轉發(fā)或者重定向會失效。因此處理為 判斷請求為ajax請求則設置返回一個狀態(tài)碼如:httpServletResponse.setStatus(666); 前端jquery.js中統(tǒng)一判斷處理。
/** * @author : wl * @Description : 攔截異常處理 * @date : 2020/7/5 15:30 */ @Slf4j public class MyWebHandlerException implements HandlerExceptionResolver { @SneakyThrows @Override public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { log.info("請求出現(xiàn)異常:" + e.getMessage()); e.printStackTrace(); // return new ModelAndView("redirect:/login"); ModelAndView modelAndView = new ModelAndView(); String type = httpServletRequest.getHeader("X-Requested-With"); if (Objects.equals(type, "XMLHttpRequest")) { //是ajax請求 httpServletResponse.setStatus(666); httpServletResponse.setContentType("text/javascript; charset=utf-8"); httpServletResponse.getWriter().write(e.getMessage()); return modelAndView; } else { modelAndView.setViewName("/login"); return modelAndView; } } }
jquery.js中添加對應狀態(tài)碼處理:
//自定義異常信息: 跳轉到登錄頁面 jQuery.ajaxSetup({ statusCode:{ 666:function(data){ alert(data.responseText); window.location.href="/login"; } } })
(6) 添加webConfig配置 實現(xiàn)WebMvcConfigurer,引入自定義的token攔截以及異常處理
/** * @author : wl * @Description :web攔截器 * @date : 2020/7/3 11:46 */ @Configuration public class webConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authenticationInterceptor()).addPathPatterns("/**"); } @Bean public AuthenticationInterceptor authenticationInterceptor() { return new AuthenticationInterceptor(); } @Bean public WebMvcConfigurer webMvcConfigurer() { WebMvcConfigurer adapter = new WebMvcConfigurer() { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("login"); registry.addViewController("/index.html").setViewName("login"); } }; return adapter; } /* * 異常攔截處理 * */ @Override public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { resolvers.add(new MyWebHandlerException()); } }
到這里基本配置基本差不多了,web應用中怎么處理呢?例如登錄功能:
登錄后端代碼:
/** * @author : wl * @Description : * @date : 2020/7/2 16:46 */ @Controller public class Login { @Autowired private SysUserService sysUserService; @Autowired private RedisUtil redisUtil; @SkipToken @RequestMapping("/login") public String toLogin() { System.out.println("跳轉到登錄頁面"); return "login"; } @SkipToken @RequestMapping("/index") public String toIndex() { System.out.println("跳轉到主頁面"); return "index"; } @SkipToken @ResponseBody @RequestMapping("/loginIn") public ResultUtil loginIn(String username, String password) { try { SysUser user = sysUserService.findUser(username, password); if (null != user) { user.setPassword(password); String token = JwtUtil.generatorToken(user, 60*60); //token 保存在redis中 redisUtil.set("ms_notify_token", token); return ResultUtil.ok().data("msg", token).message("登錄成功"); } else { return ResultUtil.error().data("msg", "error").message("用戶不存在"); } } catch (BaseException e) { return ResultUtil.error().data("msg", e.getMessage()).message("登陸失敗"); } } }
登錄前端代碼:這里需要注意的是 配置window.location.href 不生效 需要檢查是否設置為form 表單。
前端接受到token存儲在localStorage: localStorage.setItem("token",data.data.msg);
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title>登錄</title> <link href="css/bootstrap.min.css" rel="stylesheet"> <link href="css/signin.css" rel="stylesheet"> </head> <body class="text-center"> <div class="form-signin"> <img class="mb-4" th:src="@{img/my.svg}" alt="" width="80" height="80"> <h1 class="h3 mb-3 font-weight-normal">請登錄</h1> <label class="sr-only">用戶名</label> <input type="text" name="username" id="username" class="form-control" th:placeholder="用戶名" required="required" autofocus=""> <p class=""></p> <label class="sr-only">密碼</label> <input type="password" name="password" id="password" class="form-control" th:placeholder="密碼" required="required" > <button class="btn btn-lg btn-primary btn-block" th:onclick="loginIn()">登錄</button> </div> </body> <script type="text/javascript" src="/js/jquery-3.5.1.js"></script> <script type="text/javascript" src="/js/bootstrap.min.js"></script> <script> function loginIn() { debugger; var username = $('#username').val(); var password = $('#password').val(); if(""==username || ""==password){ alert("用戶信息不完整,請檢查!"); return; } $.ajax({ url: '/loginIn', type: "post", data: {'username': username, 'password': password}, success: function (data) { if (data.success) { //alert(data.message); localStorage.setItem("token",data.data.msg); location.href = "/index"; // window.location.href 不生效 檢查是否為form 表單 } else { alert(data.message) } }, error: function () { alert("登錄失敗") } }); } </script> </html>
若是需要token驗證,前端對應的ajax 請求需要加上headers 如:
//禁用,啟用 function disable_config(nid, status) { $.ajax({ url: '/notify/updateStatus', data: {'nid': nid, 'status': status}, type: "post", headers: {"token": localStorage.getItem("token")}, success: function (data) { if (data.success) { location.reload(); alert(data.message); } else { alert(data.message + ":" + data.data.msg) } } }); }
后端對應方法上需要添加注解@NeedToken 如:
/** * 禁用 、啟用 */ @NeedToken @ResponseBody @SysLogAnnotation("禁用 、啟用 配置") @RequestMapping(value = "/updateStatus") public ResultUtil updateStatus(HttpServletRequest request, String nid, int status) { String config_status = (status == 1 ? "啟用" : "禁用"); try { notifyConfigService.updateStatus(nid, status); } catch (BaseException e) { return ResultUtil.error().data("msg", e.getMessage()).message(config_status + "配置失敗"); } return ResultUtil.ok().data("msg", "success").message(config_status + "配置成功"); }
設計流程基本如上述代碼所示,詳細代碼可以參考 github地址 : https://github.com/wlcoder/ms-notify
SpringBoot 中使用 JWT案例
JWT 簡介
JWT(JSON Web Token)是一種用于身份驗證和授權的開放標準(RFC 7519),它使用JSON格式傳輸信息,可以在不同系統(tǒng)之間安全地傳遞數(shù)據(jù)。JWT由三部分組成:頭部、載荷和簽名。頭部包含算法和類型信息,載荷包含用戶信息和其他元數(shù)據(jù),簽名用于驗證JWT的真實性和完整性。JWT的優(yōu)點包括可擴展性、跨平臺、無狀態(tài)和安全性高等。它被廣泛應用于Web應用程序、移動應用程序和API等領域。
JWT 身份認證流程
- 客戶端向服務器發(fā)送用戶名和密碼,通常使用POST請求方式,將用戶名和密碼作為請求體發(fā)送給服務器。
- 服務器驗證用戶名和密碼的正確性,如果驗證通過,生成一個JWT令牌,并將令牌返回給客戶端。JWT令牌包括三部分:頭部、載荷和簽名。頭部包含令牌類型和加密算法,載荷包含用戶信息和過期時間等信息,簽名用于驗證令牌的真實性。
- 客戶端將JWT令牌保存在本地,通常使用localStorage或sessionStorage等方式保存。
- 客戶端向服務器發(fā)送請求,請求頭部包含JWT令牌。通常使用Authorization頭部字段,格式為Bearer <token>,其中<token>為JWT令牌。
- 服務器驗證JWT令牌的真實性,通常使用JWT庫進行驗證。驗證過程包括以下步驟:解析JWT令牌,驗證頭部和載荷的簽名是否正確,驗證令牌是否過期,驗證令牌是否被篡改等。如果驗證通過,返回請求結果;否則返回錯誤信息。
案例分享
下面是一個使用Spring Boot和JWT進行身份認證的示例:
1、后端代碼
1.1 添加依賴
在pom.xml文件中添加以下依賴:
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.3.1</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-core</artifactId> <version>2.3.0.1</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.3.1</version> </dependency>
1.2 添加 ShiroConfig 配置類
添加一個 ShiroConfig 類,用于配置Shiro框架的安全管理器和過濾器。
其中包含了三個方法:shiroFilterFactoryBean、defaultWebSecurityManager和realm。
- shiroFilterFactoryBean方法用于配置Shiro的過濾器,包括設置攔截規(guī)則、放行請求和設置默認的登錄頁等。
- defaultWebSecurityManager方法用于配置Shiro的安全管理器,包括設置記住我功能和將自定義域對象交給Spring管理等。
- realm方法用于創(chuàng)建自定義域對象,包括設置憑證匹配器、開啟緩存和將認證和授權緩存寫入Redis等。
整個類的作用是為Shiro框架提供安全管理和過濾器的配置。
代碼如下:
package zk.gch.temperature.shiro; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.CookieRememberMeManager; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import zk.gch.temperature.filter.JwtAuthenticationFilter; import java.util.LinkedHashMap; import javax.servlet.Filter; import java.util.HashMap; import java.util.Map; @Configuration public class ShiroConfig { // shiro中的過濾器 交給spring容器管理 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) { System.out.println("securityManager = " + securityManager); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //配置攔截的規(guī)則 Map<String, Filter> filters = new HashMap<>(); filters.put("jwt", new JwtAuthenticationFilter()); shiroFilterFactoryBean.setFilters(filters); LinkedHashMap<String, String> map = new LinkedHashMap<>(); //放行登錄請求 anon 可匿名訪問 map.put("/user/login", "anon"); map.put("/user/add", "anon"); map.put("/register.html", "anon"); // 放行靜態(tài)資源 map.put("/dist/**", "anon"); // 放行驗證碼請求 map.put("/captcha/getCaptcha", "anon"); //已登錄或“記住我”的用戶才能訪問 map.put("/**", "user"); //放行所有攜帶token請求的訪問 map.put("/**", "jwt"); // 設置默認的登錄頁 shiroFilterFactoryBean.setLoginUrl("/login.html"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } // 將安全管理器交由spring管理 @Bean public DefaultWebSecurityManager defaultWebSecurityManager(Realm realm) { DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager(); // 設置一周免登錄 CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager(); SimpleCookie rmbme = new SimpleCookie("rmbme"); rmbme.setMaxAge(60*60*24*7); cookieRememberMeManager.setCookie(rmbme); defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager); defaultWebSecurityManager.setRealm(realm); return defaultWebSecurityManager; } // 將自定義域對象 交給spring管理 @Bean public Realm realm() { CustomerRealm customerRealm = new CustomerRealm(); //設置憑證匹配器 MD5 HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher("MD5"); hashedCredentialsMatcher.setHashIterations(20); customerRealm.setCredentialsMatcher(hashedCredentialsMatcher); // 開啟shiro的緩存 開啟全局緩存 customerRealm.setCachingEnabled(true); //將認證和授權緩存寫入redis 分布式緩存 customerRealm.setCacheManager(new RedisCacheManager()); // 設置認證緩存 customerRealm.setAuthenticationCachingEnabled(true); customerRealm.setAuthenticationCacheName("authentication"); // 設置授權緩存 customerRealm.setAuthorizationCachingEnabled(true); customerRealm.setAuthorizationCacheName("authorization"); return customerRealm; } }
1.3 添加 JwtUtil 工具類
JwtUtil 工具類是一個用于生成和解析JWT(JSON Web Token)的工具類。JWT是一種用于身份驗證和授權的開放標準,它可以在客戶端和服務器之間傳遞安全可靠的信息。
該工具類中包含了生成JWT的方法createJWT(),可以設置token中要存放的數(shù)據(jù)、過期時間等信息,并使用HS256對稱加密算法簽名。同時,該工具類還包含了解析JWT的方法parseJWT(),可以解析出token中存放的數(shù)據(jù)。此外,該工具類還包含了一些常量和輔助方法,如JWT_TTL、JWT_KEY、getUUID()等。
代碼如下:
package zk.gch.temperature.utils; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.stereotype.Component; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.Base64; import java.util.Date; import java.util.UUID; @Component public class JwtUtil { //有效期為 public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000 一個小時 //設置秘鑰明文 public static final String JWT_KEY = "sangeng"; public static String getUUID(){ String token = UUID.randomUUID().toString().replaceAll("-", ""); return token; } /** * 生成jtw * @param subject token中要存放的數(shù)據(jù)(json格式) * @return */ public static String createJWT(String subject) { JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 設置過期時間 return builder.compact(); } /** * 生成jtw * @param subject token中要存放的數(shù)據(jù)(json格式) * @param ttlMillis token超時時間 * @return */ public static String createJWT(String subject, Long ttlMillis) { JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 設置過期時間 return builder.compact(); } private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; SecretKey secretKey = generalKey(); long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); if(ttlMillis==null){ ttlMillis=JwtUtil.JWT_TTL; } long expMillis = nowMillis + ttlMillis; Date expDate = new Date(expMillis); return Jwts.builder() .setId(uuid) //唯一的ID .setSubject(subject) // 主題 可以是JSON數(shù)據(jù) .setIssuer("sg") // 簽發(fā)者 .setIssuedAt(now) // 簽發(fā)時間 .signWith(signatureAlgorithm, secretKey) //使用HS256對稱加密算法簽名, 第二個參數(shù)為秘鑰 .setExpiration(expDate); } /** * 創(chuàng)建token * @param id * @param subject * @param ttlMillis * @return */ public static String createJWT(String id, String subject, Long ttlMillis) { JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 設置過期時間 return builder.compact(); } public static void main(String[] args) throws Exception { String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg"; Claims claims = parseJWT(token); System.out.println(claims); } /** * 生成加密后的秘鑰 secretKey * @return */ public static SecretKey generalKey() { byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY); SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES"); return key; } /** * 解析 * * @param jwt * @return * @throws Exception */ public static Claims parseJWT(String jwt) throws Exception { SecretKey secretKey = generalKey(); return Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(jwt) .getBody(); } }
1.4 添加 JwtAuthenticationFilter 過濾器類
JwtAuthenticationFilter 類是一個基于JWT(JSON Web Token)的身份驗證過濾器,用于在每個請求中驗證用戶的身份。它首先從請求頭中獲取Authorization字段,然后解析其中的token并驗證其有效性。如果token有效,則將用戶信息存入SecurityContextHolder中,以便后續(xù)的身份驗證。如果token無效,則返回401 Unauthorized。最后,它放行請求,使其繼續(xù)處理。
代碼如下:
package zk.gch.temperature.filter; import io.jsonwebtoken.Claims; import org.apache.shiro.web.servlet.OncePerRequestFilter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import zk.gch.temperature.utils.JwtUtil; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private JwtUtil jwtUtil; @Override protected void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; // 獲取請求頭中的Authorization字段 String header = httpRequest.getHeader("Authorization"); if (header != null && header.startsWith("Bearer ")) { // 獲取token String token = header.substring(7); System.out.println("token = " + token); try { // 解析token并驗證其有效性 Claims claims = jwtUtil.parseJWT(token); if (claims != null) { // 將用戶信息存入SecurityContextHolder中 // UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(claims.getSubject(), null, null); // SecurityContextHolder.getContext().setAuthentication(authentication); } } catch (Exception e) { // 驗證失敗,返回401 Unauthorized httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); return; } } // 放行請求 filterChain.doFilter(request, response); } }
1.5 添加 UserController 控制器類
主要代碼如下:
將JwtUtil添加到spring容器管理:
@Autowired private JwtUtil jwtUtil;
請求成功后,將token作為返回值,返回給前端:
String token = jwtUtil.createJWT(user.getId().toString(), JSON.toJSONString(user), JwtUtil.JWT_TTL);
全部代碼如下:
package zk.gch.temperature.controller; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; import zk.gch.temperature.commons.CodeMsg; import zk.gch.temperature.commons.ResponseResult; import zk.gch.temperature.dto.OhterPageDTO; import zk.gch.temperature.dto.UserLoginDTO; import zk.gch.temperature.dto.UserPageDTO; import zk.gch.temperature.dto.UserRegisterDTO; import zk.gch.temperature.entity.BasicInfo; import zk.gch.temperature.entity.Device; import zk.gch.temperature.entity.User; import zk.gch.temperature.service.UserService; import zk.gch.temperature.utils.JwtUtil; import javax.servlet.http.HttpSession; import java.util.Arrays; import java.util.List; @RestController @Api(tags="主用戶模塊") @RequestMapping("user") public class UserController { @Autowired private UserService userService; @Autowired private RedisTemplate redisTemplate; @Autowired private JwtUtil jwtUtil; @ApiOperation("用戶注冊") @PostMapping("add") public ResponseResult add(@RequestBody UserRegisterDTO user){ return userService.saveUser(user); } @ApiOperation("用戶登錄") @GetMapping("login") public ResponseResult login(UserLoginDTO userLoginDTO, HttpSession session){ System.out.println("userLoginDTO.getSessionId() = " + userLoginDTO.getSessionId()); String sessionId=""; // 1.判定用戶的驗證碼是否正確 if(userLoginDTO.getSessionId()!=null){ sessionId=userLoginDTO.getSessionId(); }else{ sessionId=session.getId(); } String code = (String) redisTemplate.opsForValue().get(sessionId); System.out.println("code = " + code); String captcha = userLoginDTO.getCaptcha(); System.out.println("captcha = " + captcha); if(code==null){ // 驗證碼失效 return new ResponseResult(CodeMsg.CAPTCHA_EXPIRE); }else{ if (code.equals(captcha)){ // 驗證碼正確 Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(userLoginDTO.getName(), userLoginDTO.getPassword()); // 判定用戶是否開啟 免登錄 String rememberMe = userLoginDTO.getRememberMe(); if("true".equals(rememberMe)){ usernamePasswordToken.setRememberMe(true); } subject.login(usernamePasswordToken); QueryWrapper<User> userQueryWrapper = new QueryWrapper<>(); userQueryWrapper.eq("name",userLoginDTO.getName()); User user = userService.getOne(userQueryWrapper); if(!userLoginDTO.getRole().equals(user.getRole())){//角色錯誤 return new ResponseResult(CodeMsg.ROLE_ERROR); } String token = jwtUtil.createJWT(user.getId().toString(), JSON.toJSONString(user), JwtUtil.JWT_TTL); return new ResponseResult(CodeMsg.SUCCESS,null,token); }else { // 驗證碼錯誤 return new ResponseResult(CodeMsg.CAPTCHA_ERROR); } } } @ApiOperation("用戶退出") @GetMapping("logout") public ResponseResult logout(){ Subject subject = SecurityUtils.getSubject(); subject.logout(); return new ResponseResult(CodeMsg.SUCCESS); } @ApiOperation("查詢用戶信息列表") @GetMapping("all") public ResponseResult selectPage(UserPageDTO userPageDTO){ return userService.selectPage(userPageDTO); } @ApiOperation(value = "根據(jù)用戶名查詢單個用戶信息") @GetMapping("getByName") public ResponseResult getByName(String name){ LambdaQueryWrapper<User> lambda = new QueryWrapper<User>().lambda(); lambda.eq((name!=null&&!"".equals(name)),User::getName,name); User user = userService.getOne(lambda); return new ResponseResult(CodeMsg.SUCCESS,null,user); } @ApiOperation(value = "根據(jù)id查詢單個用戶信息") @GetMapping("getById") public ResponseResult getById(Integer id){ User user = userService.getById(id); return new ResponseResult(CodeMsg.SUCCESS,null,user); } @ApiOperation("更新用戶信息(綁定設備)") @PutMapping("update") public ResponseResult update(@RequestBody User user){ userService.updateById(user); return new ResponseResult(CodeMsg.SUCCESS); } @ApiOperation("刪除用戶") @DeleteMapping("delete") public ResponseResult delete(Integer[] ids){ List<Integer> integers = Arrays.asList(ids); userService.removeBatchByIds(integers); return new ResponseResult(CodeMsg.SUCCESS); } //密碼重置 @ApiOperation("密碼重置") @PostMapping("/updatePwd") public ResponseResult updatePwd(Integer id){ return userService.updatePwd(id); } }
2、前端代碼
登陸成功的代碼如下:
$.get("/user/login",data,function (res) { if(res.code==0){ layer.msg(res.msg,{icon:1},function () { localStorage.setItem("token",res.data); window.location = 'index.html'; }) }else{ layer.msg(res.msg) } },"JSON")
主要就是將token存儲在localStorage中;
其他頁面請求接口時,在請求頭中添加Authorization字段;代碼如下:
var token = localStorage.getItem("token"); table.render({ elem: '#currentTableId', url: '/other/all?userName=' + localStorage.getItem("name"), toolbar: '#toolbarDemo', beforeSend: function(xhr) { // 在請求頭中添加Authorization字段 xhr.setRequestHeader("Authorization", "Bearer " + token); }, cols: [[ {type: "checkbox", width: 50}, {field: 'id', title: 'ID', width: 100, sort: true, hide: true}, {field: 'name', title: '用戶名', width: 100}, {field: 'age', title: '年齡(周歲)', width: 100}, {field: 'height', title: '身高(cm)', width: 100}, {field: 'weight', title: '體重(kg)', width: 100}, {field: 'maxTem', title: '體溫最大值(℃)', width: 130}, {field: 'minTem', title: '體溫最小值(℃)', width: 130}, {field: 'tel', title: '聯(lián)系方式', Width: 100}, {field: 'userName', title: '主用戶名', hide: true}, {field: 'deviceId', title: '設備編號', width: 100}, {field: 'deviceState', title: '設備狀態(tài)', templet: '#stateSwitch'}, {field: 'createTime', title: '創(chuàng)建時間'}, {field: 'updateTime', title: '修改時間'}, {fixed: 'right', title: '操作', width: 134, minWidth: 125, toolbar: '#barDemo'} ]], limits: [5, 10, 15, 20, 25, 50], limit: 5, page: true, skin: 'line' });
主要代碼:
獲取token:var token = localStorage.getItem("token");
在請求頭中添加Authorization字段:beforeSend: function(xhr) { // 在請求頭中添加Authorization字段 xhr.setRequestHeader("Authorization", "Bearer " + token); },
到此這篇關于SpringBoot 中使用 JWT案例的文章就介紹到這了,更多相關SpringBoot 使用 JWT內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!