Springboot 集成JWT<详细案例>

JWT的入门学习和JWTUtils封装

前面学习了JWT,并且已经成功的把JWT封装成了工具类
在现实中的前后端分离项目中,我们一般都是集合Springboot进行整合,所现在我们就与Springboot项目进行整合
JWT工作流程图
在这里插入图片描述

在登录请求上生成token并让其返回给前端

package com.jwttest.controller;

import com.jwttest.utils.JWTUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;


import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/login")
    public Map<String, Object> login(String username, String password){
        //为了清晰的实现逻辑,这里不连接数据库了,直接对字符串进行验证
        System.out.println(username+"---");
        System.out.println(password);
        Map<String, Object> map = new HashMap<>();
        if("Mark".equals(username) && "123".equals(password)){
            Map<String, String> payload = new HashMap<>();
            int id = 111111;//这个可以是从数据库中找到的用户ID
            //生成JWT令牌
            payload.put("id", Integer.toString(id));
            payload.put("username",username);
            String token = JWTUtils.getToken(payload);
            map.put("state", true);
            map.put("msg","认证成功");
            map.put("token",token);
        }else{
            map.put("state", false);
            map.put("msg", "认证失败");
        }
        return  map;
    }

    @GetMapping("/welcome")
    public Map<String,String> test(){
        //在
        Map<String, String> map = new HashMap<>();
        map.put("msg","请求成功,可以处理自己的业务逻辑");
        return map;
    }
}

写了两个请求,一个是登录请求,用户名和密码通过验证后封装成一个Token,token再和一些其它信息放回给前端。
按照正常的业务逻辑,我们知道在访问其它接口时都要携带token,我们虽然可以在每个接口上封装一个token参数,然后用前端传入过来,可是这样 太过冗余,在这里,我们可以使用拦截器来实现这个功能

新建一个拦截器

package com.jwttest.interceptors;

import ch.qos.logback.core.encoder.EchoEncoder;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jwttest.utils.JWTUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.SignatureException;
import java.util.HashMap;
import java.util.Map;

public class JWTInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String token = request.getHeader("token");
        Map<String, Object> map = new HashMap<>();
        try{
            JWTUtils.verify(token);
            return true;
        }catch (SignatureVerificationException e){
            e.printStackTrace();
            map.put("msg", "无效签名");
        }catch (TokenExpiredException e){
            map.put("msg", "token过期");
        }catch (AlgorithmMismatchException e){
            map.put("msg", "token算法不一致");
        }catch (Exception e){
            e.printStackTrace();
            map.put("msg", "token无效");
        }
        map.put("state", false);
        //将Map转为Json
        String json = new ObjectMapper().writeValueAsString(map);
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        return false;
    }
}

配置拦截器

package com.jwttest.config;

import com.jwttest.interceptors.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class Interceptors implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new JWTInterceptor())
                .addPathPatterns("/user/**")
                .excludePathPatterns("/user/login");
    }
}

进行测试

  • 访问login接口
    情况1(用户名正确,返回token)
    在这里插入图片描述
    情况2(登录失败)
    在这里插入图片描述
  • 访问其它接口
    情况1(没有携带token或者没有携带正确的token)
    在这里插入图片描述

情况2(携带了正确token)
一般情况下token是放在header,请求头上的
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,对于使用 Spring Boot 和 Shiro 框架的项目,可以参照以下步骤进行搭建: 1. 在 pom.xml 文件中添加依赖: ``` <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring-boot-web-starter</artifactId> <version>1.8.0</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.18.2</version> </dependency> ``` 其中,shiro-spring-boot-web-starter 是 Shiro 在 Spring Boot 中的集成包,java-jwt 是用于生成和解析 JWT 的库。 2. 在 application.properties 或 application.yml 文件中配置 Shiro: ``` # 配置 Shiro 的加密方式 shiro.hashAlgorithmName=md5 shiro.hashIterations=2 # 配置 JWT 的过期时间 jwt.expireTime=1800 # 配置 Shiro 的登录路径和首页路径 shiro.loginUrl=/login shiro.successUrl=/index # 配置 Shiro 的过滤器链 shiro.filterChainDefinitionMap= \ /static/** = anon \ /login = anon \ /logout = logout \ /** = authc ``` 其中,shiro.hashAlgorithmName 和 shiro.hashIterations 是 Shiro 的加密方式,jwt.expireTime 是 JWT 的过期时间,shiro.loginUrl 和 shiro.successUrl 是 Shiro 的登录路径和首页路径,shiro.filterChainDefinitionMap 是 Shiro 的过滤器链。 3. 创建 Shiro 配置类,用于配置 Shiro 的各种组件: ``` @Configuration public class ShiroConfig { // 配置 SecurityManager @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myRealm()); // 配置自定义 Realm securityManager.setRememberMeManager(cookieRememberMeManager()); // 配置 RememberMeManager securityManager.setSessionManager(sessionManager()); // 配置 SessionManager return securityManager; } // 配置自定义 Realm @Bean public MyRealm myRealm() { return new MyRealm(); } // 配置 RememberMeManager @Bean public RememberMeManager cookieRememberMeManager() { CookieRememberMeManager rememberMeManager = new CookieRememberMeManager(); rememberMeManager.setCookie(rememberMeCookie()); return rememberMeManager; } // 配置 RememberMeCookie @Bean public SimpleCookie rememberMeCookie() { SimpleCookie cookie = new SimpleCookie("rememberMe"); cookie.setMaxAge(86400); // 设置 Cookie 的过期时间为一天 return cookie; } // 配置 SessionManager @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionIdCookieEnabled(true); sessionManager.setSessionIdUrlRewritingEnabled(false); sessionManager.setSessionDAO(redisSessionDAO()); // 配置 RedisSessionDAO sessionManager.setGlobalSessionTimeout(1800000); // 设置 Session 的过期时间为半小时 sessionManager.setDeleteInvalidSessions(true); sessionManager.setSessionValidationSchedulerEnabled(true); return sessionManager; } // 配置 RedisSessionDAO @Bean public RedisSessionDAO redisSessionDAO() { RedisSessionDAO redisSessionDAO = new RedisSessionDAO(); redisSessionDAO.setRedisManager(redisManager()); // 配置 RedisManager redisSessionDAO.setKeyPrefix("shiro:session:"); redisSessionDAO.setExpire(1800); // 设置 Session 的过期时间为半小时 return redisSessionDAO; } // 配置 RedisManager @Bean public RedisManager redisManager() { RedisManager redisManager = new RedisManager(); redisManager.setHost("localhost"); redisManager.setPort(6379); redisManager.setTimeout(0); return redisManager; } // 配置 ShiroFilterFactoryBean @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean() { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager()); factoryBean.setLoginUrl("/login"); factoryBean.setSuccessUrl("/index"); factoryBean.setUnauthorizedUrl("/unauthorized"); factoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap()); return factoryBean; } // 配置 ShiroFilterChainDefinition @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); chainDefinition.addPathDefinition("/static/**", "anon"); chainDefinition.addPathDefinition("/login", "anon"); chainDefinition.addPathDefinition("/logout", "logout"); chainDefinition.addPathDefinition("/**", "authc"); return chainDefinition; } } ``` 其中,myRealm() 方法用于配置自定义 Realm,cookieRememberMeManager() 方法用于配置 RememberMeManager 和 RememberMeCookie,sessionManager() 方法用于配置 SessionManager 和 RedisSessionDAO,redisManager() 方法用于配置 RedisManager,shiroFilterFactoryBean() 方法用于配置 ShiroFilterFactoryBean,shiroFilterChainDefinition() 方法用于配置 ShiroFilterChainDefinition。 4. 创建自定义 Realm 类,用于实现用户的认证和授权逻辑: ``` public class MyRealm extends AuthorizingRealm { // 配置加密方式 @Override public void setCredentialsMatcher(CredentialsMatcher credentialsMatcher) { HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); matcher.setHashAlgorithmName("md5"); matcher.setHashIterations(2); super.setCredentialsMatcher(matcher); } // 用户认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken upToken = (UsernamePasswordToken) token; String username = upToken.getUsername(); String password = new String(upToken.getPassword()); // 根据用户名和密码查询用户信息 User user = userService.findByUsernameAndPassword(username, password); if (user == null) { throw new UnknownAccountException("用户名或密码错误"); } SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName()); return info; } // 用户授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); User user = (User) principals.getPrimaryPrincipal(); // 根据用户角色查询权限信息 List<Role> roles = userService.findRolesByUsername(user.getUsername()); for (Role role : roles) { info.addRole(role.getName()); List<Permission> permissions = role.getPermissions(); for (Permission permission : permissions) { info.addStringPermission(permission.getName()); } } return info; } } ``` 在自定义 Realm 类中,通过 setCredentialsMatcher() 方法配置加密方式,通过 doGetAuthenticationInfo() 方法实现用户的认证逻辑,通过 doGetAuthorizationInfo() 方法实现用户的授权逻辑。 5. 创建登录控制器,用于处理用户的登录和注销: ``` @Controller public class LoginController { @GetMapping("/login") public String login() { return "login"; } @PostMapping("/login") public String login(String username, String password, boolean rememberMe, HttpServletRequest request) { try { // 将用户名和密码封装成 UsernamePasswordToken 对象 UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 设置 RememberMe token.setRememberMe(rememberMe); // 执行登录操作 SecurityUtils.getSubject().login(token); return "redirect:/index"; } catch (UnknownAccountException e) { request.setAttribute("error", "用户名不存在"); } catch (IncorrectCredentialsException e) { request.setAttribute("error", "密码错误"); } catch (LockedAccountException e) { request.setAttribute("error", "账号已被锁定"); } catch (AuthenticationException e) { request.setAttribute("error", "登录失败:" + e.getMessage()); } return "login"; } @GetMapping("/logout") public String logout() { SecurityUtils.getSubject().logout(); return "redirect:/login"; } } ``` 在登录控制器中,通过 login() 方法实现用户的登录操作,通过 logout() 方法实现用户的注销操作。 6. 在页面中添加登录界面和首页,例如: ``` <!-- 登录界面 --> <form method="post" action="/login"> <div class="form-group"> <label for="username">用户名:</label> <input type="text" id="username" name="username" class="form-control" required> </div> <div class="form-group"> <label for="password">密码:</label> <input type="password" id="password" name="password" class="form-control" required> </div> <div class="form-check"> <input type="checkbox" id="rememberMe" name="rememberMe" class="form-check-input"> <label for="rememberMe" class="form-check-label">记住我</label> </div> <button type="submit" class="btn btn-primary">登录</button> </form> <!-- 首页 --> <h1>欢迎登录</h1> <p><a href="/logout">退出登录</a></p> ``` 通过以上步骤,就可以使用 Spring Boot 和 Shiro 框架来实现用户的认证和授权了,当然,在具体实现时,还需要针对自己的业务需求进行一些调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值