0


SpringBoot 中使用 JWT 案例分享详解

在这里插入图片描述

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。
🍎个人主页:Java Fans的博客
🍊个人信条:不迁怒,不贰过。小知识,大智慧。
💞当前专栏:Java案例分享专栏
✨特色专栏:国学周更-心性养成之路
🥭本文内容: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框架提供安全管理和过滤器的配置。

代码如下:

packagezk.gch.temperature.shiro;importorg.apache.shiro.authc.credential.HashedCredentialsMatcher;importorg.apache.shiro.realm.Realm;importorg.apache.shiro.spring.web.ShiroFilterFactoryBean;importorg.apache.shiro.web.mgt.CookieRememberMeManager;importorg.apache.shiro.web.mgt.DefaultWebSecurityManager;importorg.apache.shiro.web.servlet.SimpleCookie;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importzk.gch.temperature.filter.JwtAuthenticationFilter;importjava.util.LinkedHashMap;importjavax.servlet.Filter;importjava.util.HashMap;importjava.util.Map;@ConfigurationpublicclassShiroConfig{//  shiro中的过滤器 交给spring容器管理@BeanpublicShiroFilterFactoryBeanshiroFilterFactoryBean(DefaultWebSecurityManager securityManager){System.out.println("securityManager = "+ securityManager);ShiroFilterFactoryBean shiroFilterFactoryBean =newShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);//配置拦截的规则Map<String,Filter> filters =newHashMap<>();
        filters.put("jwt",newJwtAuthenticationFilter());
        shiroFilterFactoryBean.setFilters(filters);LinkedHashMap<String,String> map =newLinkedHashMap<>();//放行登录请求  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管理@BeanpublicDefaultWebSecurityManagerdefaultWebSecurityManager(Realm realm){DefaultWebSecurityManager defaultWebSecurityManager =newDefaultWebSecurityManager();// 设置一周免登录CookieRememberMeManager cookieRememberMeManager =newCookieRememberMeManager();SimpleCookie rmbme =newSimpleCookie("rmbme");
        rmbme.setMaxAge(60*60*24*7);
        cookieRememberMeManager.setCookie(rmbme);
        defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager);

        defaultWebSecurityManager.setRealm(realm);return defaultWebSecurityManager;}// 将自定义域对象 交给spring管理@BeanpublicRealmrealm(){CustomerRealm customerRealm =newCustomerRealm();//设置凭证匹配器  MD5HashedCredentialsMatcher hashedCredentialsMatcher =newHashedCredentialsMatcher("MD5");
        hashedCredentialsMatcher.setHashIterations(20);
        customerRealm.setCredentialsMatcher(hashedCredentialsMatcher);// 开启shiro的缓存  开启全局缓存
        customerRealm.setCachingEnabled(true);//将认证和授权缓存写入redis   分布式缓存
        customerRealm.setCacheManager(newRedisCacheManager());// 设置认证缓存
        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()等。

代码如下:

packagezk.gch.temperature.utils;importio.jsonwebtoken.Claims;importio.jsonwebtoken.JwtBuilder;importio.jsonwebtoken.Jwts;importio.jsonwebtoken.SignatureAlgorithm;importorg.springframework.stereotype.Component;importjavax.crypto.SecretKey;importjavax.crypto.spec.SecretKeySpec;importjava.util.Base64;importjava.util.Date;importjava.util.UUID;@ComponentpublicclassJwtUtil{//有效期为publicstaticfinalLong JWT_TTL =60*60*1000L;// 60 * 60 *1000  一个小时//设置秘钥明文publicstaticfinalString JWT_KEY ="sangeng";publicstaticStringgetUUID(){String token = UUID.randomUUID().toString().replaceAll("-","");return token;}/**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @return
     */publicstaticStringcreateJWT(String subject){JwtBuilder builder =getJwtBuilder(subject,null,getUUID());// 设置过期时间return builder.compact();}/**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @param ttlMillis token超时时间
     * @return
     */publicstaticStringcreateJWT(String subject,Long ttlMillis){JwtBuilder builder =getJwtBuilder(subject, ttlMillis,getUUID());// 设置过期时间return builder.compact();}privatestaticJwtBuildergetJwtBuilder(String subject,Long ttlMillis,String uuid){SignatureAlgorithm signatureAlgorithm =SignatureAlgorithm.HS256;SecretKey secretKey =generalKey();long nowMillis =System.currentTimeMillis();Date now =newDate(nowMillis);if(ttlMillis==null){
            ttlMillis=JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate =newDate(expMillis);returnJwts.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
     */publicstaticStringcreateJWT(String id,String subject,Long ttlMillis){JwtBuilder builder =getJwtBuilder(subject, ttlMillis, id);// 设置过期时间return builder.compact();}publicstaticvoidmain(String[] args)throwsException{String token ="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";Claims claims =parseJWT(token);System.out.println(claims);}/**
     * 生成加密后的秘钥 secretKey
     * @return
     */publicstaticSecretKeygeneralKey(){byte[] encodedKey =Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key =newSecretKeySpec(encodedKey,0, encodedKey.length,"AES");return key;}/**
     * 解析
     *
     * @param jwt
     * @return
     * @throws Exception
     */publicstaticClaimsparseJWT(String jwt)throwsException{SecretKey secretKey =generalKey();returnJwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}}

1.4 添加 JwtAuthenticationFilter 过滤器类

JwtAuthenticationFilter 类是一个基于JWT(JSON Web Token)的身份验证过滤器,用于在每个请求中验证用户的身份。它首先从请求头中获取Authorization字段,然后解析其中的token并验证其有效性。如果token有效,则将用户信息存入SecurityContextHolder中,以便后续的身份验证。如果token无效,则返回401 Unauthorized。最后,它放行请求,使其继续处理。

代码如下:

packagezk.gch.temperature.filter;importio.jsonwebtoken.Claims;importorg.apache.shiro.web.servlet.OncePerRequestFilter;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importzk.gch.temperature.utils.JwtUtil;importjavax.servlet.FilterChain;importjavax.servlet.ServletException;importjavax.servlet.ServletRequest;importjavax.servlet.ServletResponse;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.IOException;@ComponentpublicclassJwtAuthenticationFilterextendsOncePerRequestFilter{@AutowiredprivateJwtUtil jwtUtil;@OverrideprotectedvoiddoFilterInternal(ServletRequest request,ServletResponse response,FilterChain filterChain)throwsServletException,IOException{HttpServletRequest httpRequest =(HttpServletRequest) request;HttpServletResponse httpResponse =(HttpServletResponse) response;// 获取请求头中的Authorization字段String header = httpRequest.getHeader("Authorization");if(header !=null&& header.startsWith("Bearer ")){// 获取tokenString 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容器管理:

@AutowiredprivateJwtUtil jwtUtil;

请求成功后,将token作为返回值,返回给前端:

String token = jwtUtil.createJWT(user.getId().toString(), JSON.toJSONString(user),JwtUtil.JWT_TTL);

全部代码如下:

packagezk.gch.temperature.controller;importcom.alibaba.fastjson.JSON;importcom.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;importcom.baomidou.mybatisplus.core.conditions.query.QueryWrapper;importio.swagger.annotations.Api;importio.swagger.annotations.ApiOperation;importorg.apache.shiro.SecurityUtils;importorg.apache.shiro.authc.UsernamePasswordToken;importorg.apache.shiro.subject.Subject;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.web.bind.annotation.*;importzk.gch.temperature.commons.CodeMsg;importzk.gch.temperature.commons.ResponseResult;importzk.gch.temperature.dto.OhterPageDTO;importzk.gch.temperature.dto.UserLoginDTO;importzk.gch.temperature.dto.UserPageDTO;importzk.gch.temperature.dto.UserRegisterDTO;importzk.gch.temperature.entity.BasicInfo;importzk.gch.temperature.entity.Device;importzk.gch.temperature.entity.User;importzk.gch.temperature.service.UserService;importzk.gch.temperature.utils.JwtUtil;importjavax.servlet.http.HttpSession;importjava.util.Arrays;importjava.util.List;@RestController@Api(tags="主用户模块")@RequestMapping("user")publicclassUserController{@AutowiredprivateUserService userService;@AutowiredprivateRedisTemplate redisTemplate;@AutowiredprivateJwtUtil jwtUtil;@ApiOperation("用户注册")@PostMapping("add")publicResponseResultadd(@RequestBodyUserRegisterDTO user){return userService.saveUser(user);}@ApiOperation("用户登录")@GetMapping("login")publicResponseResultlogin(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){//  验证码失效returnnewResponseResult(CodeMsg.CAPTCHA_EXPIRE);}else{if(code.equals(captcha)){// 验证码正确Subject subject =SecurityUtils.getSubject();UsernamePasswordToken usernamePasswordToken =newUsernamePasswordToken(userLoginDTO.getName(), userLoginDTO.getPassword());// 判定用户是否开启 免登录String rememberMe = userLoginDTO.getRememberMe();if("true".equals(rememberMe)){
                    usernamePasswordToken.setRememberMe(true);}
                subject.login(usernamePasswordToken);QueryWrapper<User> userQueryWrapper =newQueryWrapper<>();
                userQueryWrapper.eq("name",userLoginDTO.getName());User user = userService.getOne(userQueryWrapper);if(!userLoginDTO.getRole().equals(user.getRole())){//角色错误returnnewResponseResult(CodeMsg.ROLE_ERROR);}String token = jwtUtil.createJWT(user.getId().toString(), JSON.toJSONString(user),JwtUtil.JWT_TTL);returnnewResponseResult(CodeMsg.SUCCESS,null,token);}else{// 验证码错误returnnewResponseResult(CodeMsg.CAPTCHA_ERROR);}}}@ApiOperation("用户退出")@GetMapping("logout")publicResponseResultlogout(){Subject subject =SecurityUtils.getSubject();
        subject.logout();returnnewResponseResult(CodeMsg.SUCCESS);}@ApiOperation("查询用户信息列表")@GetMapping("all")publicResponseResultselectPage(UserPageDTO userPageDTO){return userService.selectPage(userPageDTO);}@ApiOperation(value ="根据用户名查询单个用户信息")@GetMapping("getByName")publicResponseResultgetByName(String name){LambdaQueryWrapper<User> lambda =newQueryWrapper<User>().lambda();
        lambda.eq((name!=null&&!"".equals(name)),User::getName,name);User user = userService.getOne(lambda);returnnewResponseResult(CodeMsg.SUCCESS,null,user);}@ApiOperation(value ="根据id查询单个用户信息")@GetMapping("getById")publicResponseResultgetById(Integer id){User user = userService.getById(id);returnnewResponseResult(CodeMsg.SUCCESS,null,user);}@ApiOperation("更新用户信息(绑定设备)")@PutMapping("update")publicResponseResultupdate(@RequestBodyUser user){
        userService.updateById(user);returnnewResponseResult(CodeMsg.SUCCESS);}@ApiOperation("删除用户")@DeleteMapping("delete")publicResponseResultdelete(Integer[] ids){List<Integer> integers =Arrays.asList(ids);
        userService.removeBatchByIds(integers);returnnewResponseResult(CodeMsg.SUCCESS);}//密码重置@ApiOperation("密码重置")@PostMapping("/updatePwd")publicResponseResultupdatePwd(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); },

  码文不易,本篇文章就介绍到这里,如果想要学习更多Java系列知识点击关注博主,博主带你零基础学习Java知识。与此同时,对于日常生活有困扰的朋友,欢迎阅读我的第四栏目:《国学周更—心性养成之路》,学习技术的同时,我们也注重了心性的养成。

在这里插入图片描述

标签: spring boot java 后端

本文转载自: https://blog.csdn.net/hh867308122/article/details/131191030
版权归原作者 Java Fans 所有, 如有侵权,请联系我们删除。

“SpringBoot 中使用 JWT 案例分享详解”的评论:

还没有评论