您现在的位置是:首页 > 编程 > 

SpringBoot集成Shiro权限+Jwt认证

2025-07-27 15:06:23
SpringBoot集成Shiro权限+Jwt认证 背景为什么要使用Shiro? 随大流吧,虽然自己也可以基于自定义注解+实现和Shiro一样的功能,但是为了适用于业界的规范,所以集成这个大家都能看得懂,而且Shiro也相对简单。 为什么要用Jwt? 传统的session模式越来越少,而且大多数系统都是微服务多客户端的,所以无状态的登陆更符合现阶段的业务架构。 开始 本案例基于Sprin

SpringBoot集成Shiro权限+Jwt认证

背景

  • 为什么要使用Shiro

随大流吧,虽然自己也可以基于自定义注解+实现和Shiro一样的功能,但是为了适用于业界的规范,所以集成这个大家都能看得懂,而且Shiro也相对简单。

  • 为什么要用Jwt

传统的session模式越来越少,而且大多数系统都是微服务多客户端的,所以无状态的登陆更符合现阶段的业务架构。

开始

本案例基于SpringBoot 2.5.X + Shiro 1.8 + hutool的Jwt

pom.xml
代码语言:javascript代码运行次数:0运行复制
xml 代码解读复制代码    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.5.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.5.4</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.24</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.8.0</version>
        </dependency>
    </dependencies>
RespeMessage返回消息体
代码语言:javascript代码运行次数:0运行复制
typescript 代码解读复制代码@Data
public class RespeMessage<T> {

    private Boolean success = Boolean.TRUE;

    private String code;

    private String message;

    private T data;

    public static <T> RespeMessage<T> success(T data){
        RespeMessage<T> respeMessage = new RespeMessage<>();
        respeMessage.setCode("200");
        respeMessage.setMessage("操作成功");
        respeMessage.setData(data);
        return respeMessage;
    }

    public static <T> RespeMessage<T> fail(String message){
        RespeMessage<T> respeMessage = new RespeMessage<>();
        respeMessage.setSuccess(Boolean.FALSE);
        respeMessage.setCode("500");
        respeMessage.setMessage(message);
        return respeMessage;
    }

}
GlobalExceptionHandler全局异常处理
代码语言:javascript代码运行次数:0运行复制
less 代码解读复制代码@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = )
    @RespeBody
    public RespeMessage<String> handleAllExcepti(Exception ex, WebRequest request) {
        // 处理异常
        ("业务异常",ex);
        return RespeMessage.fail(ex.getMessage());
    }

}
JwtUtils工具类
代码语言:javascript代码运行次数:0运行复制
typescript 代码解读复制代码public class JwtUtils {

    private static final byte[] KEY = "ABADEXU".getBytes();

    public static String createToken(Map<String, Object> payload){
        return (payload, JwtUtils.KEY);
    }

    public static JWT parseToken(String token){
        return JWTUtil.parseToken(token);
    }

    public static Boolean verify(String token){
        return JWTUtil.verify(token, JwtUtils.KEY);
    }
}
JwtToken 认证dto
代码语言:javascript代码运行次数:0运行复制
typescript 代码解读复制代码@Data
public class JwtToken implements AuthenticationToken {

    /** JWT 认证串 */
    private String jwt;

    public JwtToken(String jwt) {
        this.jwt = jwt;
    }

    @Override
    public Object getPrincipal() {
        return jwt;
    }

    @Override
    public Object getCredentials() {
        return jwt;
    }

}
JwtFilter 权限认证过滤器

拦截请求接口的

代码语言:javascript代码运行次数:0运行复制
scala 代码解读复制代码@Slf4j
public class JwtFilter extends AccessControlFilter {


    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletRespe respe, Object mappedValue) throws Exception {
        HttpServletRequest httpServletRequest   = (HttpServletRequest) request;
        String jwt                              = httpServletRequest.getHeader("Authorization");
        if(StrUtil.isotBlank(jwt)){
            getSubject(request, respe).login(new JwtToken(jwt));
            return true;
        }
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletRespe respe) throws Exception {
        throw new RuntimeException("身份验证异常");
    }

}
JwtRealm 授权领域

授权流程JwtFilter#isAccessAllowed -> JwtRealm#supports -> JwtRealm#doGetAuthenticationInfo -> JwtRealm#doGetAuthorizationInfo

代码语言:javascript代码运行次数:0运行复制
java 代码解读复制代码@Slf4j
public class JwtRealm extends AuthorizingRealm {

    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("验证jwt token 权限");
        String jwt = principalCollection.getPrimaryPrincipal().toString();
        // 这里一般就从redis中拿用户的权限信息,案例就直接写死了
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        // 设置角
        simpleAuthorizationInfo.addRole("admin");
        simpleAuthorizationInfo.addRole("user");
        // 设置权限
        simpleAuthorizationInfo.addStringPermission("user:add");
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("验证jwt token 有效性");
        String jwt = authenticationToken.getPrincipal().toString();
        // 从redis查询jwt token是否还存在,是否有效
        if(!Boolean.(JwtUtils.verify(jwt))){
            throw new RuntimeException("jwt token 失效");
        }
        JWT parseToken = JwtUtils.parseToken(jwt);
        Object expiryTime = parseToken.getPayload("expiryTime");
        // 验证token是否过期
        return new SimpleAuthenticationInfo(jwt, jwt, this.getClass().getame());
    }
}
ShiroConfig 配置
代码语言:javascript代码运行次数:0运行复制
typescript 代码解读复制代码@Configuration
public class ShiroConfig {

    @Bean
    public JwtRealm jwtRealm(){
        return new JwtRealm();
    }

    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(jwtRealm());
        /*
         * 关闭shiro自带的session,详情见文档
         * .html#SessionManagement-StatelessApplicati%28Sessionless%29
         */
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        defaultWebSecurityManager.setSubjectDAO(subjectDAO);
        return defaultWebSecurityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(defaultWebSecurityManager());
        // 未授权跳转
        shiroFilter.setUnauthorizedUrl("/unauthorized");
        Map<String, Filter> filterMap = new HashMap<>();
        //
        filterMap.put("jwt", new JwtFilter());
        //
        shiroFilter.setFilters(filterMap);

        Map<String, String> filterRuleMap = new LinkedHashMap<>();
        // 匿名访问
        filterRuleMap.put("/error", "anon");
        filterRuleMap.put("/login", "anon");
        filterRuleMap.put("/logout", "anon");
        filterRuleMap.put("/unauthorized", "anon");
        // 登录并具有 admin 角
        // filterRuleMap.put("/index/admin", "authc,roles[admin]");
        // filterRuleMap.put("/index/admin", "jwt,roles[admin]");
        // 通过jwt校验,需登录才能访问(自行实现逻辑)
        filterRuleMap.put("/**", "jwt");
        //
        shiroFilter.setFilterChainDefinitionMap(filterRuleMap);
        //
        return shiroFilter;
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissi)
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setUsePrefix(true);
        return advisorAutoProxyCreator;
    }

}
测试相关
LoginController 登陆接口
代码语言:javascript代码运行次数:0运行复制
typescript 代码解读复制代码@RestController
public class LoginController {

    @GetMapping("login")
    public Object login(String username){
        Map<String, Object> payload = new HashMap<>();
        payload.put("username", username);
        // 设置0分钟后过期
        payload.put("expiryTime", DateUtil.date().offset(DateField.MIUTE, 0));
        return (payload);
    }

}
UserController 权限验证接口
代码语言:javascript代码运行次数:0运行复制
less 代码解读复制代码@Slf4j
@RestController
@RequestMapping(value = "user")
public class UserController {

    @RequiresPermissi(value = {"user:view"})
    @GetMapping(value = "page")
    public Object page(){
        log.info("page");
        return "SUCCESS";
    }

    @RequiresPermissi(value = {"user:add"})
    @GetMapping(value = "add")
    public Object add(){
        log.info("add");
        return "SUCCESS";
    }

}

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/biancheng/1176615.html

相关标签:无
上传时间: 2025-07-21 18:20:18
留言与评论(共有 9 条评论)
本站网友 新手网
3分钟前 发表
JwtUtils.KEY); } }JwtToken 认证dto类代码语言:javascript代码运行次数:0运行复制typescript 代码解读复制代码@Data public class JwtToken implements AuthenticationToken { /** JWT 认证串 */ private String jwt; public JwtToken(String jwt) { this.jwt = jwt; } @Override public Object getPrincipal() { return jwt; } @Override public Object getCredentials() { return jwt; } }JwtFilter 权限认证过滤器 拦截请求接口的 代码语言:javascript代码运行次数:0运行复制scala 代码解读复制代码@Slf4j public class JwtFilter extends AccessControlFilter { @Override protected boolean isAccessAllowed(ServletRequest request
本站网友 肥西二手房信息
29分钟前 发表
详情见文档 * .html#SessionManagement-StatelessApplicati%28Sessionless%29 */ DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO(); DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator(); defaultSessionStorageEvaluator.setSessionStorageEnabled(false); subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator); defaultWebSecurityManager.setSubjectDAO(subjectDAO); return defaultWebSecurityManager; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(defaultWebSecurityManager()); // 未授权跳转 shiroFilter.setUnauthorizedUrl("/unauthorized"); Map<String
本站网友 上海订酒店
8分钟前 发表
是否有效 if(!Boolean.(JwtUtils.verify(jwt))){ throw new RuntimeException("jwt token 失效"); } JWT parseToken = JwtUtils.parseToken(jwt); Object expiryTime = parseToken.getPayload("expiryTime"); // 验证token是否过期 return new SimpleAuthenticationInfo(jwt
本站网友 哈尔滨广厦学院
15分钟前 发表
案例就直接写死了 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); // 设置角 simpleAuthorizationInfo.addRole("admin"); simpleAuthorizationInfo.addRole("user"); // 设置权限 simpleAuthorizationInfo.addStringPermission("user
本站网友 海伦二手房
15分钟前 发表
为什么要用Jwt? 传统的session模式越来越少
本站网友 大白兔奶糖的热量
17分钟前 发表
JwtUtils.KEY); } }JwtToken 认证dto类代码语言:javascript代码运行次数:0运行复制typescript 代码解读复制代码@Data public class JwtToken implements AuthenticationToken { /** JWT 认证串 */ private String jwt; public JwtToken(String jwt) { this.jwt = jwt; } @Override public Object getPrincipal() { return jwt; } @Override public Object getCredentials() { return jwt; } }JwtFilter 权限认证过滤器 拦截请求接口的 代码语言:javascript代码运行次数:0运行复制scala 代码解读复制代码@Slf4j public class JwtFilter extends AccessControlFilter { @Override protected boolean isAccessAllowed(ServletRequest request
本站网友 肺癌的治疗
29分钟前 发表
需登录才能访问(自行实现逻辑) filterRuleMap.put("/**"
本站网友 网盘直链
15分钟前 发表
"anon"); filterRuleMap.put("/login"