news 2026/7/8 15:59:47

SpringBoot集成shiro

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot集成shiro

SpringBoot集成shiro

数据库设计

  • sh_user:用户表,一个用户可以有多个角色
  • sh_role: 角色表,一个角色可以有多个资源
  • sh_resource:资源表
  • sh_user_role:用户角色中间表
  • sh_role_resource:角色资源中间表

首先自定义realm抽象类并实现

/* by 01130.hk - online tools website : 01130.hk/zh/tiaoseban.html */ package com.itheima.shiro.core; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import javax.annotation.PostConstruct; /** * @Description:自定义realm的抽象类 */ public abstract class ShiroDbRealm extends AuthorizingRealm { /** * @Description 认证方法 * @param token token对象 * @return 认证信息 */ @Override protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException ; /** * @Description 授权方法 * @param principals 令牌对象 * @return 授权信息 */ @Override protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals); /** * @Description 自定义密码比较器 * @param * @return */ @PostConstruct public abstract void initCredentialsMatcher(); }

实现这个抽象类

/* by 01130.hk - online tools website : 01130.hk/zh/tiaoseban.html */ package com.itheima.shiro.core.impl; import com.itheima.shiro.constant.SuperConstant; import com.itheima.shiro.core.ShiroDbRealm; import com.itheima.shiro.core.base.ShiroUser; import com.itheima.shiro.core.base.SimpleToken; import com.itheima.shiro.core.bridge.UserBridgeService; import com.itheima.shiro.pojo.User; import com.itheima.shiro.utils.BeanConv; import com.itheima.shiro.utils.EmptyUtil; import org.apache.shiro.authc.*; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; /** * @Description:自定义realm的抽象类实现 */ public class ShiroDbRealmImpl extends ShiroDbRealm { @Autowired UserBridgeService userBridgeService; /** * @Description 认证方法 * @param token token对象 * @return 认证信息 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //token令牌信息,这里的SimpleToken继承了UsernamePasswordToken SimpleToken simpleToken = (SimpleToken) token; //根据登录名查询user对象 User user = userBridgeService.findUserByLoginName(simpleToken.getUsername()); if (EmptyUtil.isNullOrEmpty(user)){ throw new UnknownAccountException("账号不存在!"); } //构建认证令牌对象(将User对象转换成ShiroUser对象,ShiroUser类比Users实体类多了资源列表等属性) ShiroUser shiroUser = BeanConv.toBean(user, ShiroUser.class); // 根据用户id获取资源列表,并设置到shiroUser对象 shiroUser.setResourceIds(userBridgeService.findResourcesIds(shiroUser.getId())); String slat = shiroUser.getSalt(); String password = shiroUser.getPassWord(); //构建认证信息对象:1、令牌对象 2、密文密码 3、加密因子 4、当前realm的名称 return new SimpleAuthenticationInfo(shiroUser, password, ByteSource.Util.bytes(slat), getName()); } /** * @Description 授权方法 * @param principals 令牌对象 * @return 授权信息 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // 获得令牌对象 ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal(); return userBridgeService.getAuthorizationInfo(shiroUser); } /** * @Description 自定义密码比较器 * @param * @return */ @Override public void initCredentialsMatcher() { //指定密码算法 HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(SuperConstant.HASH_ALGORITHM); //指定迭代次数 hashedCredentialsMatcher.setHashIterations(SuperConstant.HASH_INTERATIONS); //生效密码比较器 setCredentialsMatcher(hashedCredentialsMatcher); } }

SimpleToken类(说明)

package com.itheima.shiro.core.base; import org.apache.shiro.authc.UsernamePasswordToken; /** * @Description 自定义tooken */ public class SimpleToken extends UsernamePasswordToken { /** serialVersionUID */ private static final long serialVersionUID = -4849823851197352099L; private String tokenType; private String quickPassword; /** * Constructor for SimpleToken * @param tokenType */ public SimpleToken(String tokenType, String username,String password) { super(username,password); this.tokenType = tokenType; } public SimpleToken(String tokenType, String username,String password,String quickPassword) { super(username,password); this.tokenType = tokenType; this.quickPassword = quickPassword; } public String getTokenType() { return tokenType; } public void setTokenType(String tokenType) { this.tokenType = tokenType; } public String getQuickPassword() { return quickPassword; } public void setQuickPassword(String quickPassword) { this.quickPassword = quickPassword; } }

userBridgeService接口(说明)

package com.itheima.shiro.core.bridge; import com.itheima.shiro.core.base.ShiroUser; import com.itheima.shiro.pojo.User; import org.apache.shiro.authz.AuthorizationInfo; import java.util.List; /** * @Description:用户信息桥接(后期会做缓存) */ public interface UserBridgeService { /** * @Description 查找用户信息 * @param loginName 用户名称 * @return user对象 */ User findUserByLoginName(String loginName); /** * @Description 查询资源ids * @param userId 用户id * @return 资源id集合 */ List<String> findResourcesIds(String userId); /** * @Description 鉴权方法 * @param shiroUser 令牌对象 * @return 鉴权信息 */ AuthorizationInfo getAuthorizationInfo(ShiroUser shiroUser); /** * @Description 查询用户对应角色标识list * @param userId 用户id * @return 角色标识集合 */ List<String> findRoleList(String userId); /** * @Description 查询用户对应资源标识list * @param userId 用户id * @return 资源标识集合 */ List<String> findResourcesList(String userId); }

ShiroUser类(说明)

package com.itheima.shiro.core.base; import com.itheima.shiro.utils.ToString; import lombok.Data; import java.util.List; /** * @Description 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息 */ @Data public class ShiroUser extends ToString { /** serialVersionUID */ private static final long serialVersionUID = -5024855628064590607L; /** * 主键 */ private String id; /** * 登录名称 */ private String loginName; /** * 真实姓名 */ private String realName; /** * 昵称 */ private String nickName; /** * 密码 */ private String passWord; /** * 加密因子 */ private String salt; /** * 性别 */ private Integer sex; /** * 邮箱 */ private String zipcode; /** * 地址 */ private String address; /** * 固定电话 */ private String tel; /** * 电话 */ private String mobil; /** * 邮箱 */ private String email; /** * 职务 */ private String duties; /** * 排序 */ private Integer sortNo; /** * 是否有效 */ private String enableFlag; /** * @Description 资源列表 */ private List<String> resourceIds; public ShiroUser() { super(); } public ShiroUser(String id, String loginName) { super(); this.id = id; this.loginName = loginName; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((email == null) ? 0 : email.hashCode()); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((loginName == null) ? 0 : loginName.hashCode()); result = prime * result + ((mobil == null) ? 0 : mobil.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ShiroUser other = (ShiroUser) obj; if (email == null) { if (other.email != null) return false; } else if (!email.equals(other.email)) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (loginName == null) { if (other.loginName != null) return false; } else if (!loginName.equals(other.loginName)) return false; if (mobil == null) { if (other.mobil != null) return false; } else if (!mobil.equals(other.mobil)) return false; return true; } }

SuperConstant类(说明)

package com.itheima.shiro.constant; /** * * @Description 静态变量 */ public class SuperConstant { /** * 常量是 */ public static final String YES = "YES"; /** * 常量否 */ public static final String NO = "NO"; /** * 匿名用户ID */ public static final String ANON_ID = "-1"; /** * 树形根节点父Id */ public static final String ROOT_PARENT_ID = "-1"; /** * 树形根节点Id */ public static final String ROOT_ID = "1"; /** * 匿名用户登录名 */ public static final String ANON_LOGIN_NAME = "ANONYMITY"; /** * 匿名用户真实名 */ public static final String ANON_REAL_NAME = "匿名"; /** * hash算法 */ public static final String HASH_ALGORITHM = "SHA-1"; /** * 计算次数 */ public static final int HASH_INTERATIONS = 1024; }

ShiroConfig配置

package com.itheima.shiro.config; import com.itheima.shiro.core.ShiroDbRealm; import com.itheima.shiro.core.filter.RolesOrAuthorizationFilter; import com.itheima.shiro.core.impl.ShiroDbRealmImpl; import com.itheima.shiro.properties.PropertiesUtil; import lombok.extern.log4j.Log4j2; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.apache.shiro.web.servlet.SimpleCookie; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; import javax.servlet.Filter; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * @Description:权限管理配置类 */ @Configuration // 不配置扫描也可以 @ComponentScan(basePackages = "com.itheima.shiro.core") @Log4j2 public class ShiroConfig { //创建cookie对象 @Bean(name = "simpleCookie") public SimpleCookie simpleCookie(){ SimpleCookie simpleCookie = new SimpleCookie(); simpleCookie.setName("ShiroSession"); return simpleCookie; } //创建权限管理器 @Bean("securityManager") public DefaultWebSecurityManager defaultWebSecurityManager(){ DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //管理realm securityManager.setRealm(shiroDbRealm()); //管理会话 securityManager.setSessionManager(sessionManager()); return securityManager; } //自定义realm @Bean("shiroDbRealm") public ShiroDbRealm shiroDbRealm(){ return new ShiroDbRealmImpl(); } //会话管理器 @Bean("sessionManager") public DefaultWebSessionManager sessionManager(){ DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); //关闭会话更新 sessionManager.setSessionValidationSchedulerEnabled(false); //生效cookie sessionManager.setSessionIdCookieEnabled(true); //指定cookie的生成策略 sessionManager.setSessionIdCookie(simpleCookie()); //指定全局会话超时时间 sessionManager.setGlobalSessionTimeout(3600000); return sessionManager; } //创建生命周期的管理 @Bean("lifecycleBeanPostProcessor") public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){ return new LifecycleBeanPostProcessor(); } //aop增强(使用注解鉴权方式) /** * @Description AOP式方法级权限检查 */ @Bean @DependsOn("lifecycleBeanPostProcessor") public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); defaultAdvisorAutoProxyCreator.setProxyTargetClass(true); return defaultAdvisorAutoProxyCreator; } /** * @Description 配合DefaultAdvisorAutoProxyCreator事项开启注解权限校验 */ @Bean public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor(); aasa.setSecurityManager(defaultWebSecurityManager()); return aasa; } /** * @Description 过滤器链定义 */ private Map<String,String> filterChainDefinitionMap(){ // 读取authentication.properties配置文件中的过滤器链定义 // PropertiesUtil是读取配置文件的工具类 List<Object> list = PropertiesUtil.propertiesShiro.getKeyList(); // 必须创建一个有序的map Map<String,String> map = new LinkedHashMap<>(); for (Object o : list) { String key = o.toString(); String val = PropertiesUtil.getShiroValue(key); map.put(key, val); } return map; } /** * @Description 加载自定义过滤器 */ private Map<String, Filter> filters(){ Map<String,Filter> map = new HashMap<>(); // 这里的key表示过滤器的名称 map.put("roles-or", new RolesOrAuthorizationFilter()); return map; } //shiro过滤器管理 @Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilterFactoryBean(){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager()); //过滤器,指定自己的过滤器 shiroFilterFactoryBean.setFilters(filters()); //过滤器链 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap()); //登录页面 shiroFilterFactoryBean.setLoginUrl("/login"); //未授权页面 shiroFilterFactoryBean.setUnauthorizedUrl("/login"); return shiroFilterFactoryBean; } }

PropertiesUtil类(说明)

package com.itheima.shiro.properties; import com.itheima.shiro.utils.EmptyUtil; import lombok.extern.log4j.Log4j2; /** * @Description 读取Properties的工具类 */ @Log4j2 public class PropertiesUtil { public static LinkProperties propertiesShiro = new LinkProperties(); /** * 读取properties配置文件信息 */ static { String sysName = System.getProperty("sys.name"); if (EmptyUtil.isNullOrEmpty(sysName)) { sysName = "application.properties"; } else { sysName += ".properties"; } try { propertiesShiro.load(PropertiesUtil.class.getClassLoader() .getResourceAsStream("authentication.properties")); } catch (Exception e) { log.warn("资源路径中不存在authentication.properties权限文件,忽略读取!"); } } /** * 根据key得到value的值 */ public static String getShiroValue(String key) { return propertiesShiro.getProperty(key); } }

authentication.properties配置文件

/static/**=anon #登录链接不拦截 /login/**=anon #访问/resource/**需要有admin的角色 /resource/**=roles[dev,SuperAdmin] #其他链接是需要登录的 /**=authc ```

说明:过滤器链由路径+过滤器组成,它是自上而下有序的,一个一个去匹配的。如果上面匹配失败,则不会匹配下面的内容。

自定义过滤器

package com.itheima.shiro.core.filter; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.CollectionUtils; import org.apache.shiro.web.filter.authz.AuthorizationFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.IOException; import java.util.Set; /** * @Description:角色or判断过滤器,只要当前用户包含传入的角色集合中的一个角色,就判断通过 */ public class RolesOrAuthorizationFilter extends AuthorizationFilter { //TODO - complete JavaDoc @SuppressWarnings({"unchecked"}) public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException { Subject subject = getSubject(request, response); String[] rolesArray = (String[]) mappedValue; if (rolesArray == null || rolesArray.length == 0) { //no roles specified, so nothing to check - allow access. return true; } Set<String> roles = CollectionUtils.asSet(rolesArray); for (String role : roles) { boolean flag = subject.hasRole(role); if (flag){ return flag; } } return false; } }

加载自定义过滤器

在 ShiroConfig类中配置如下:

/** * @Description 加载自定义过滤器 */ private Map<String, Filter> filters(){ Map<String,Filter> map = new HashMap<>(); // 这里的key表示过滤器的名称 map.put("roles-or", new RolesOrAuthorizationFilter()); return map; } //shiro过滤器管理 @Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilterFactoryBean(){ ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager()); //过滤器,指定自定义的过滤器 shiroFilterFactoryBean.setFilters(filters()); //过滤器链 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap()); //登录页面 shiroFilterFactoryBean.setLoginUrl("/login"); //未授权页面 shiroFilterFactoryBean.setUnauthorizedUrl("/login"); return shiroFilterFactoryBean; }

测试自定义过滤器

在 authentication.properties配置文件中修改内容如下:

#访问/resource/**需要有SuperAdmin或者dev的角色 /resource/**=roles-or[dev,SuperAdmin]

注解方式鉴权

本文来自博客园,作者:NE_STOP,转载请注明原文链接:https://www.cnblogs.com/alineverstop/p/19475427

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 18:00:45

AI骨骼点检测省钱攻略:按需GPU比包月服务器省2000+

AI骨骼点检测省钱攻略&#xff1a;按需GPU比包月服务器省2000 1. 为什么你需要这份攻略 作为一名大学实验室的研究人员&#xff0c;你可能正在为每月2000元的GPU服务器租金发愁。但实际情况是&#xff0c;这些服务器可能80%的时间都处于闲置状态。通过改用按需付费的GPU方案&…

作者头像 李华
网站建设 2026/7/1 22:25:53

Redis ZSET在电商排行榜中的实战应用

快速体验 打开 InsCode(快马)平台 https://www.inscode.net输入框内输入如下内容&#xff1a; 设计一个电商商品销量排行榜系统&#xff0c;使用Redis ZSET存储商品ID和销量数据。要求实现&#xff1a;1. 每日销量更新&#xff1b;2. 实时获取TOP 10热销商品&#xff1b;3. 支…

作者头像 李华
网站建设 2026/7/2 0:19:02

Z-Image-ComfyUI团队协作:3人共享的低成本AI创作方案

Z-Image-ComfyUI团队协作&#xff1a;3人共享的低成本AI创作方案 1. 为什么选择Z-Image-ComfyUI进行团队协作&#xff1f; 对于大学生小组作业或小型创意团队来说&#xff0c;AI创作工具往往面临两个痛点&#xff1a;一是专业软件学习成本高&#xff0c;二是硬件资源投入大。…

作者头像 李华
网站建设 2026/7/2 1:46:27

Z-Image-ComfyUI云端方案:比本地快3倍的AI绘画体验

Z-Image-ComfyUI云端方案&#xff1a;比本地快3倍的AI绘画体验 引言&#xff1a;当游戏美术遇上AI绘画 作为一名游戏美术设计师&#xff0c;你是否经常遇到这样的困境&#xff1a;角色原画反复修改&#xff0c;每次渲染都要等待漫长的5分钟&#xff1f;公司电脑配置有限&…

作者头像 李华
网站建设 2026/6/26 9:20:06

智能自动打码技术揭秘:AI人脸隐私卫士核心算法解析

智能自动打码技术揭秘&#xff1a;AI人脸隐私卫士核心算法解析 1. 技术背景与隐私保护挑战 在社交媒体、公共数据发布和智能监控日益普及的今天&#xff0c;图像中的人脸信息泄露风险急剧上升。一张看似普通的大合照&#xff0c;可能包含数十个未经同意暴露身份的个体。传统手…

作者头像 李华
网站建设 2026/7/4 11:25:28

揭秘HMAC验证机制:5步实现高安全性的消息签名与校验

第一章&#xff1a;HMAC验证机制的核心原理HMAC&#xff08;Hash-based Message Authentication Code&#xff09;是一种基于哈希函数和密钥的消息认证码机制&#xff0c;用于确保数据的完整性与真实性。其核心思想是通过一个密钥与消息共同参与哈希运算&#xff0c;生成唯一认…

作者头像 李华