找回密码
 立即注册
首页 业界区 业界 SpringBoot集成shiro

SpringBoot集成shiro

奸轲嫣 2026-1-14 03:35:01
SpringBoot集成shiro

数据库设计

  • sh_user:用户表,一个用户可以有多个角色
  • sh_role: 角色表,一个角色可以有多个资源
  • sh_resource:资源表
  • sh_user_role:用户角色中间表
  • sh_role_resource:角色资源中间表
首先自定义realm抽象类并实现
  1. package com.itheima.shiro.core;
  2. import org.apache.shiro.authc.AuthenticationException;
  3. import org.apache.shiro.authc.AuthenticationInfo;
  4. import org.apache.shiro.authc.AuthenticationToken;
  5. import org.apache.shiro.authz.AuthorizationInfo;
  6. import org.apache.shiro.realm.AuthorizingRealm;
  7. import org.apache.shiro.subject.PrincipalCollection;
  8. import javax.annotation.PostConstruct;
  9. /**
  10. * @Description:自定义realm的抽象类
  11. */
  12. public abstract class ShiroDbRealm extends AuthorizingRealm {
  13.     /**
  14.      * @Description 认证方法
  15.      * @param token token对象
  16.      * @return 认证信息
  17.      */
  18.     @Override
  19.     protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException ;
  20.     /**
  21.      * @Description 授权方法
  22.      * @param principals 令牌对象
  23.      * @return 授权信息
  24.      */
  25.     @Override
  26.     protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals);
  27.    /**
  28.     * @Description 自定义密码比较器
  29.     * @param
  30.     * @return
  31.     */
  32.    @PostConstruct
  33.    public abstract void initCredentialsMatcher();
  34. }
复制代码
实现这个抽象类
  1. package com.itheima.shiro.core.impl;
  2. import com.itheima.shiro.constant.SuperConstant;
  3. import com.itheima.shiro.core.ShiroDbRealm;
  4. import com.itheima.shiro.core.base.ShiroUser;
  5. import com.itheima.shiro.core.base.SimpleToken;
  6. import com.itheima.shiro.core.bridge.UserBridgeService;
  7. import com.itheima.shiro.pojo.User;
  8. import com.itheima.shiro.utils.BeanConv;
  9. import com.itheima.shiro.utils.EmptyUtil;
  10. import org.apache.shiro.authc.*;
  11. import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
  12. import org.apache.shiro.authz.AuthorizationInfo;
  13. import org.apache.shiro.subject.PrincipalCollection;
  14. import org.apache.shiro.util.ByteSource;
  15. import org.springframework.beans.factory.annotation.Autowired;
  16. /**
  17. * @Description:自定义realm的抽象类实现
  18. */
  19. public class ShiroDbRealmImpl extends ShiroDbRealm {
  20.     @Autowired
  21.     UserBridgeService userBridgeService;
  22.      /**
  23.      * @Description 认证方法
  24.      * @param token token对象
  25.      * @return 认证信息
  26.      */
  27.     @Override
  28.     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  29.         //token令牌信息,这里的SimpleToken继承了UsernamePasswordToken
  30.         SimpleToken simpleToken = (SimpleToken) token;
  31.         //根据登录名查询user对象
  32.         User user = userBridgeService.findUserByLoginName(simpleToken.getUsername());
  33.         if (EmptyUtil.isNullOrEmpty(user)){
  34.             throw new UnknownAccountException("账号不存在!");
  35.         }
  36.         //构建认证令牌对象(将User对象转换成ShiroUser对象,ShiroUser类比Users实体类多了资源列表等属性)
  37.         ShiroUser shiroUser = BeanConv.toBean(user, ShiroUser.class);
  38.         // 根据用户id获取资源列表,并设置到shiroUser对象
  39.         shiroUser.setResourceIds(userBridgeService.findResourcesIds(shiroUser.getId()));
  40.         String slat  = shiroUser.getSalt();
  41.         String password = shiroUser.getPassWord();
  42.         //构建认证信息对象:1、令牌对象 2、密文密码  3、加密因子 4、当前realm的名称
  43.         return new SimpleAuthenticationInfo(shiroUser, password, ByteSource.Util.bytes(slat), getName());
  44.     }
  45.      /**
  46.      * @Description 授权方法
  47.      * @param principals 令牌对象
  48.      * @return 授权信息
  49.      */
  50.     @Override
  51.     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  52.         // 获得令牌对象
  53.         ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
  54.         return userBridgeService.getAuthorizationInfo(shiroUser);
  55.     }
  56.      /**
  57.     * @Description 自定义密码比较器
  58.     * @param
  59.     * @return
  60.     */
  61.     @Override
  62.     public void initCredentialsMatcher() {
  63.         //指定密码算法
  64.         HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(SuperConstant.HASH_ALGORITHM);
  65.         //指定迭代次数
  66.         hashedCredentialsMatcher.setHashIterations(SuperConstant.HASH_INTERATIONS);
  67.         //生效密码比较器
  68.         setCredentialsMatcher(hashedCredentialsMatcher);
  69.     }
  70. }
复制代码
SimpleToken类(说明)
  1. package com.itheima.shiro.core.base;
  2. import org.apache.shiro.authc.UsernamePasswordToken;
  3. /**
  4. * @Description 自定义tooken
  5. */
  6. public class SimpleToken extends UsernamePasswordToken {
  7.    
  8.     /** serialVersionUID */
  9.     private static final long serialVersionUID = -4849823851197352099L;
  10.     private String tokenType;
  11.    
  12.     private String quickPassword;
  13.     /**
  14.      * Constructor for SimpleToken
  15.      * @param tokenType
  16.      */
  17.     public SimpleToken(String tokenType, String username,String password) {
  18.        super(username,password);
  19.        this.tokenType = tokenType;
  20.     }
  21.    
  22.     public SimpleToken(String tokenType, String username,String password,String quickPassword) {
  23.        super(username,password);
  24.        this.tokenType = tokenType;
  25.        this.quickPassword = quickPassword;
  26.     }
  27.     public String getTokenType() {
  28.        return tokenType;
  29.     }
  30.     public void setTokenType(String tokenType) {
  31.        this.tokenType = tokenType;
  32.     }
  33.     public String getQuickPassword() {
  34.        return quickPassword;
  35.     }
  36.     public void setQuickPassword(String quickPassword) {
  37.        this.quickPassword = quickPassword;
  38.     }
  39.    
  40.    
  41. }
复制代码
userBridgeService接口(说明)
  1. package com.itheima.shiro.core.bridge;
  2. import com.itheima.shiro.core.base.ShiroUser;
  3. import com.itheima.shiro.pojo.User;
  4. import org.apache.shiro.authz.AuthorizationInfo;
  5. import java.util.List;
  6. /**
  7. * @Description:用户信息桥接(后期会做缓存)
  8. */
  9. public interface UserBridgeService {
  10.     /**
  11.      * @Description 查找用户信息
  12.      * @param loginName 用户名称
  13.      * @return user对象
  14.      */
  15.     User findUserByLoginName(String loginName);
  16.     /**
  17.      * @Description 查询资源ids
  18.      * @param userId 用户id
  19.      * @return 资源id集合
  20.      */
  21.     List<String> findResourcesIds(String userId);
  22.     /**
  23.      * @Description 鉴权方法
  24.      * @param shiroUser 令牌对象
  25.      * @return 鉴权信息
  26.      */
  27.     AuthorizationInfo getAuthorizationInfo(ShiroUser shiroUser);
  28.     /**
  29.      * @Description 查询用户对应角色标识list
  30.      * @param userId 用户id
  31.      * @return 角色标识集合
  32.      */
  33.     List<String> findRoleList(String userId);
  34.     /**
  35.      * @Description 查询用户对应资源标识list
  36.      * @param userId 用户id
  37.      * @return 资源标识集合
  38.      */
  39.     List<String> findResourcesList(String userId);
  40. }
复制代码
ShiroUser类(说明)
  1. package com.itheima.shiro.core.base;
  2. import com.itheima.shiro.utils.ToString;
  3. import lombok.Data;
  4. import java.util.List;
  5. /**
  6. * @Description 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息
  7. */
  8. @Data
  9. public class  ShiroUser extends ToString {
  10.     /** serialVersionUID */
  11.     private static final long serialVersionUID = -5024855628064590607L;
  12.     /**
  13.      * 主键
  14.      */
  15.     private String id;
  16.     /**
  17.      * 登录名称
  18.      */
  19.     private String loginName;
  20.     /**
  21.      * 真实姓名
  22.      */
  23.     private String realName;
  24.     /**
  25.      * 昵称
  26.      */
  27.     private String nickName;
  28.     /**
  29.      * 密码
  30.      */
  31.     private String passWord;
  32.     /**
  33.      * 加密因子
  34.      */
  35.     private String salt;
  36.     /**
  37.      * 性别
  38.      */
  39.     private Integer sex;
  40.     /**
  41.      * 邮箱
  42.      */
  43.     private String zipcode;
  44.     /**
  45.      * 地址
  46.      */
  47.     private String address;
  48.     /**
  49.      * 固定电话
  50.      */
  51.     private String tel;
  52.     /**
  53.      * 电话
  54.      */
  55.     private String mobil;
  56.     /**
  57.      * 邮箱
  58.      */
  59.     private String email;
  60.     /**
  61.      * 职务
  62.      */
  63.     private String duties;
  64.     /**
  65.      * 排序
  66.      */
  67.     private Integer sortNo;
  68.     /**
  69.      * 是否有效
  70.      */
  71.     private String enableFlag;
  72.    
  73.     /**
  74.      * @Description 资源列表
  75.      */
  76.     private List<String> resourceIds;
  77.     public ShiroUser() {
  78.        super();
  79.     }
  80.     public ShiroUser(String id, String loginName) {
  81.        super();
  82.        this.id = id;
  83.        this.loginName = loginName;
  84.     }
  85.     @Override
  86.     public int hashCode() {
  87.        final int prime = 31;
  88.        int result = 1;
  89.        result = prime * result + ((email == null) ? 0 : email.hashCode());
  90.        result = prime * result + ((id == null) ? 0 : id.hashCode());
  91.        result = prime * result
  92.              + ((loginName == null) ? 0 : loginName.hashCode());
  93.        result = prime * result + ((mobil == null) ? 0 : mobil.hashCode());
  94.        return result;
  95.     }
  96.     @Override
  97.     public boolean equals(Object obj) {
  98.        if (this == obj)
  99.           return true;
  100.        if (obj == null)
  101.           return false;
  102.        if (getClass() != obj.getClass())
  103.           return false;
  104.        ShiroUser other = (ShiroUser) obj;
  105.        if (email == null) {
  106.           if (other.email != null)
  107.              return false;
  108.        } else if (!email.equals(other.email))
  109.           return false;
  110.        if (id == null) {
  111.           if (other.id != null)
  112.              return false;
  113.        } else if (!id.equals(other.id))
  114.           return false;
  115.        if (loginName == null) {
  116.           if (other.loginName != null)
  117.              return false;
  118.        } else if (!loginName.equals(other.loginName))
  119.           return false;
  120.        if (mobil == null) {
  121.           if (other.mobil != null)
  122.              return false;
  123.        } else if (!mobil.equals(other.mobil))
  124.           return false;
  125.        return true;
  126.     }
  127.    
  128.    
  129. }
复制代码
SuperConstant类(说明)
  1. package com.itheima.shiro.constant;
  2. /**
  3. *
  4. * @Description 静态变量
  5. */
  6. public class SuperConstant {
  7.    
  8.     /**
  9.      * 常量是
  10.      */
  11.     public static final String YES = "YES";
  12.     /**
  13.      * 常量否
  14.      */
  15.     public static final String NO = "NO";
  16.    
  17.     /**
  18.      * 匿名用户ID
  19.      */
  20.     public static final String ANON_ID = "-1";
  21.    
  22.     /**
  23.      * 树形根节点父Id
  24.      */
  25.     public static final String ROOT_PARENT_ID = "-1";
  26.    
  27.     /**
  28.      * 树形根节点Id
  29.      */
  30.     public static final String ROOT_ID = "1";
  31.     /**
  32.      * 匿名用户登录名
  33.      */
  34.     public static final String ANON_LOGIN_NAME = "ANONYMITY";
  35.     /**
  36.      * 匿名用户真实名
  37.      */
  38.     public static final String ANON_REAL_NAME = "匿名";
  39.    
  40.    
  41.     /**
  42.      * hash算法
  43.      */
  44.     public static final String HASH_ALGORITHM = "SHA-1";
  45.    
  46.     /**
  47.      * 计算次数
  48.      */
  49.     public static final int HASH_INTERATIONS = 1024;
  50. }
复制代码
ShiroConfig配置
  1. package com.itheima.shiro.config;
  2. import com.itheima.shiro.core.ShiroDbRealm;
  3. import com.itheima.shiro.core.filter.RolesOrAuthorizationFilter;
  4. import com.itheima.shiro.core.impl.ShiroDbRealmImpl;
  5. import com.itheima.shiro.properties.PropertiesUtil;
  6. import lombok.extern.log4j.Log4j2;
  7. import org.apache.shiro.spring.LifecycleBeanPostProcessor;
  8. import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
  9. import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
  10. import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
  11. import org.apache.shiro.web.servlet.SimpleCookie;
  12. import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
  13. import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
  14. import org.springframework.context.annotation.Bean;
  15. import org.springframework.context.annotation.ComponentScan;
  16. import org.springframework.context.annotation.Configuration;
  17. import org.springframework.context.annotation.DependsOn;
  18. import javax.servlet.Filter;
  19. import java.util.HashMap;
  20. import java.util.LinkedHashMap;
  21. import java.util.List;
  22. import java.util.Map;
  23. /**
  24. * @Description:权限管理配置类
  25. */
  26. @Configuration
  27. // 不配置扫描也可以
  28. @ComponentScan(basePackages = "com.itheima.shiro.core")
  29. @Log4j2
  30. public class ShiroConfig {
  31.     //创建cookie对象
  32.     @Bean(name = "simpleCookie")
  33.     public SimpleCookie simpleCookie(){
  34.         SimpleCookie simpleCookie = new SimpleCookie();
  35.         simpleCookie.setName("ShiroSession");
  36.         return  simpleCookie;
  37.     }
  38.     //创建权限管理器
  39.     @Bean("securityManager")
  40.     public DefaultWebSecurityManager defaultWebSecurityManager(){
  41.         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
  42.         //管理realm
  43.         securityManager.setRealm(shiroDbRealm());
  44.         //管理会话
  45.         securityManager.setSessionManager(sessionManager());
  46.         return securityManager;
  47.     }
  48.     //自定义realm
  49.     @Bean("shiroDbRealm")
  50.     public ShiroDbRealm shiroDbRealm(){
  51.         return new  ShiroDbRealmImpl();
  52.     }
  53.     //会话管理器
  54.     @Bean("sessionManager")
  55.     public DefaultWebSessionManager sessionManager(){
  56.         DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
  57.         //关闭会话更新
  58.         sessionManager.setSessionValidationSchedulerEnabled(false);
  59.         //生效cookie
  60.         sessionManager.setSessionIdCookieEnabled(true);
  61.         //指定cookie的生成策略
  62.         sessionManager.setSessionIdCookie(simpleCookie());
  63.         //指定全局会话超时时间
  64.         sessionManager.setGlobalSessionTimeout(3600000);
  65.         return sessionManager;
  66.     }
  67.     //创建生命周期的管理
  68.     @Bean("lifecycleBeanPostProcessor")
  69.     public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
  70.         return  new LifecycleBeanPostProcessor();
  71.     }
  72.     //aop增强(使用注解鉴权方式)
  73.     /**
  74.      * @Description AOP式方法级权限检查
  75.      */
  76.     @Bean
  77.     @DependsOn("lifecycleBeanPostProcessor")
  78.     public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
  79.         DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
  80.         defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
  81.         return defaultAdvisorAutoProxyCreator;
  82.     }
  83.     /**
  84.      * @Description 配合DefaultAdvisorAutoProxyCreator事项开启注解权限校验
  85.      */
  86.     @Bean
  87.     public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {
  88.         AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
  89.         aasa.setSecurityManager(defaultWebSecurityManager());
  90.         return aasa;
  91.     }
  92.     /**
  93.      * @Description 过滤器链定义
  94.      */
  95.     private Map<String,String> filterChainDefinitionMap(){
  96.         // 读取authentication.properties配置文件中的过滤器链定义
  97.         // PropertiesUtil是读取配置文件的工具类
  98.        List<Object> list =  PropertiesUtil.propertiesShiro.getKeyList();
  99.        // 必须创建一个有序的map
  100.         Map<String,String> map = new LinkedHashMap<>();
  101.         for (Object o : list) {
  102.             String key = o.toString();
  103.             String val = PropertiesUtil.getShiroValue(key);
  104.             map.put(key, val);
  105.         }
  106.         return map;
  107.     }
  108.     /**
  109.      * @Description 加载自定义过滤器
  110.      */
  111.     private Map<String, Filter> filters(){
  112.         Map<String,Filter> map = new HashMap<>();
  113.         // 这里的key表示过滤器的名称
  114.         map.put("roles-or", new RolesOrAuthorizationFilter());
  115.         return map;
  116.     }
  117.     //shiro过滤器管理
  118.     @Bean("shiroFilter")
  119.     public ShiroFilterFactoryBean shiroFilterFactoryBean(){
  120.         ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
  121.         shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager());
  122.         //过滤器,指定自己的过滤器
  123.         shiroFilterFactoryBean.setFilters(filters());
  124.         //过滤器链
  125.         shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap());
  126.         //登录页面
  127.         shiroFilterFactoryBean.setLoginUrl("/login");
  128.         //未授权页面
  129.         shiroFilterFactoryBean.setUnauthorizedUrl("/login");
  130.         return shiroFilterFactoryBean;
  131.     }
  132. }
复制代码
PropertiesUtil类(说明)
  1. package com.itheima.shiro.properties;
  2. import com.itheima.shiro.utils.EmptyUtil;
  3. import lombok.extern.log4j.Log4j2;
  4. /**
  5. * @Description 读取Properties的工具类
  6. */
  7. @Log4j2
  8. public class PropertiesUtil {
  9.     public static LinkProperties propertiesShiro = new LinkProperties();
  10.     /**
  11.      * 读取properties配置文件信息
  12.      */
  13.     static {
  14.         String sysName = System.getProperty("sys.name");
  15.         if (EmptyUtil.isNullOrEmpty(sysName)) {
  16.             sysName = "application.properties";
  17.         } else {
  18.             sysName += ".properties";
  19.         }
  20.         try {
  21.             propertiesShiro.load(PropertiesUtil.class.getClassLoader()
  22.                     .getResourceAsStream("authentication.properties"));
  23.         } catch (Exception e) {
  24.             log.warn("资源路径中不存在authentication.properties权限文件,忽略读取!");
  25.         }
  26.     }
  27.     /**
  28.      * 根据key得到value的值
  29.      */
  30.     public static String getShiroValue(String key) {
  31.         return propertiesShiro.getProperty(key);
  32.     }
  33. }
复制代码
authentication.properties配置文件
  1. /static/**=anon
  2. #登录链接不拦截
  3. /login/**=anon
  4. #访问/resource/**需要有admin的角色
  5. /resource/**=roles[dev,SuperAdmin]
  6. #其他链接是需要登录的
  7. /**=authc ```
复制代码
说明:过滤器链由路径+过滤器组成,它是自上而下有序的,一个一个去匹配的。如果上面匹配失败,则不会匹配下面的内容。
自定义过滤器
  1. package com.itheima.shiro.core.filter;
  2. import org.apache.shiro.subject.Subject;
  3. import org.apache.shiro.util.CollectionUtils;
  4. import org.apache.shiro.web.filter.authz.AuthorizationFilter;
  5. import javax.servlet.ServletRequest;
  6. import javax.servlet.ServletResponse;
  7. import java.io.IOException;
  8. import java.util.Set;
  9. /**
  10. * @Description:角色or判断过滤器,只要当前用户包含传入的角色集合中的一个角色,就判断通过
  11. */
  12. public class RolesOrAuthorizationFilter extends AuthorizationFilter {
  13.     //TODO - complete JavaDoc
  14.     @SuppressWarnings({"unchecked"})
  15.     public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {
  16.         Subject subject = getSubject(request, response);
  17.         String[] rolesArray = (String[]) mappedValue;
  18.         if (rolesArray == null || rolesArray.length == 0) {
  19.             //no roles specified, so nothing to check - allow access.
  20.             return true;
  21.         }
  22.         Set<String> roles = CollectionUtils.asSet(rolesArray);
  23.         for (String role : roles) {
  24.             boolean flag = subject.hasRole(role);
  25.             if (flag){
  26.                 return flag;
  27.             }
  28.         }
  29.         return false;
  30.     }
  31. }
复制代码
加载自定义过滤器

在 ShiroConfig类中配置如下:
  1. /**
  2. * @Description 加载自定义过滤器
  3. */
  4. private Map<String, Filter> filters(){
  5.     Map<String,Filter> map = new HashMap<>();
  6.     // 这里的key表示过滤器的名称
  7.     map.put("roles-or", new RolesOrAuthorizationFilter());
  8.     return map;
  9. }
  10. //shiro过滤器管理
  11. @Bean("shiroFilter")
  12. public ShiroFilterFactoryBean shiroFilterFactoryBean(){
  13.     ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
  14.     shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager());
  15.     //过滤器,指定自定义的过滤器
  16.     shiroFilterFactoryBean.setFilters(filters());
  17.     //过滤器链
  18.     shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap());
  19.     //登录页面
  20.     shiroFilterFactoryBean.setLoginUrl("/login");
  21.     //未授权页面
  22.     shiroFilterFactoryBean.setUnauthorizedUrl("/login");
  23.     return shiroFilterFactoryBean;
  24. }
复制代码
测试自定义过滤器

在 authentication.properties配置文件中修改内容如下:
  1. #访问/resource/**需要有SuperAdmin或者dev的角色
  2. /resource/**=roles-or[dev,SuperAdmin]
复制代码
注解方式鉴权

1.png


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

2026-1-27 17:32:37

举报

您需要登录后才可以回帖 登录 | 立即注册