SpringBoot整合JWT实战教程
作者:山阴路的秋天
SpringBoot整合JWT实战详解
jwt 介绍就不多说了,下面通过代码演示开发过程中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"; //创建秘钥 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(); } /** * 根据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)添加拦截器异常处理 :出现异常直接跳转到登录页面 ,这里有个坑,前端为ajax请求时候 使用转发或者重定向会失效。因此处理为 判断请求为ajax请求则设置返回一个状态码如:httpServletResponse.setStatus(666); 前端jquery.js中统一判断处理。
/** * @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("请求出现异常:" + 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中添加对应状态码处理:
//自定义异常信息: 跳转到登录页面 jQuery.ajaxSetup({ statusCode:{ 666:function(data){ alert(data.responseText); window.location.href="/login"; } } })
(6) 添加webConfig配置 实现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格式传输信息,可以在不同系统之间安全地传递数据。JWT由三部分组成:头部、载荷和签名。头部包含算法和类型信息,载荷包含用户信息和其他元数据,签名用于验证JWT的真实性和完整性。JWT的优点包括可扩展性、跨平台、无状态和安全性高等。它被广泛应用于Web应用程序、移动应用程序和API等领域。
JWT 身份认证流程
- 客户端向服务器发送用户名和密码,通常使用POST请求方式,将用户名和密码作为请求体发送给服务器。
- 服务器验证用户名和密码的正确性,如果验证通过,生成一个JWT令牌,并将令牌返回给客户端。JWT令牌包括三部分:头部、载荷和签名。头部包含令牌类型和加密算法,载荷包含用户信息和过期时间等信息,签名用于验证令牌的真实性。
- 客户端将JWT令牌保存在本地,通常使用localStorage或sessionStorage等方式保存。
- 客户端向服务器发送请求,请求头部包含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的过滤器,包括设置拦截规则、放行请求和设置默认的登录页等。
- defaultWebSecurityManager方法用于配置Shiro的安全管理器,包括设置记住我功能和将自定义域对象交给Spring管理等。
- realm方法用于创建自定义域对象,包括设置凭证匹配器、开启缓存和将认证和授权缓存写入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); //配置拦截的规则 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"); // 放行静态资源 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中要存放的数据、过期时间等信息,并使用HS256对称加密算法签名。同时,该工具类还包含了解析JWT的方法parseJWT(),可以解析出token中存放的数据。此外,该工具类还包含了一些常量和辅助方法,如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中要存放的数据(json格式) * @return */ public static String createJWT(String subject) { JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间 return builder.compact(); } /** * 生成jtw * @param subject token中要存放的数据(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数据 .setIssuer("sg") // 签发者 .setIssuedAt(now) // 签发时间 .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥 .setExpiration(expDate); } /** * 创建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中,以便后续的身份验证。如果token无效,则返回401 Unauthorized。最后,它放行请求,使其继续处理。
代码如下:
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 = "根据用户名查询单个用户信息") @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 = "根据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: '联系方式', Width: 100}, {field: 'userName', title: '主用户名', hide: true}, {field: 'deviceId', title: '设备编号', width: 100}, {field: 'deviceState', title: '设备状态', templet: '#stateSwitch'}, {field: 'createTime', title: '创建时间'}, {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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!