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

SpringBoot集成shiro

后沛若 2026-1-14 03:25:03
SpringBoot集成shiro

数据库设计

  • sh_user:用户表,一个用户可以有多个角色
  • sh_role: 角色表,一个角色可以有多个资源
  • sh_resource:资源表
  • sh_user_role:用户角色中间表
  • sh_role_resource:角色资源中间表
首先自定义realm抽象类并实现
  1. 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();}
复制代码
实现这个抽象类
  1. 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类(说明)
  1. 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接口(说明)
  1. 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 findResourcesIds(String userId);    /**     * @Description 鉴权方法     * @param shiroUser 令牌对象     * @return 鉴权信息     */    AuthorizationInfo getAuthorizationInfo(ShiroUser shiroUser);    /**     * @Description 查询用户对应角色标识list     * @param userId 用户id     * @return 角色标识集合     */    List findRoleList(String userId);    /**     * @Description 查询用户对应资源标识list     * @param userId 用户id     * @return 资源标识集合     */    List findResourcesList(String userId);}
复制代码
ShiroUser类(说明)
  1. package com.itheima.shiro.core.base;import com.itheima.shiro.utils.ToString;import lombok.Data;import java.util.List;/** * @Description 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息 */@Datapublic 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 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类(说明)
  1. 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配置
  1. 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")@Log4j2public 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 filterChainDefinitionMap(){        // 读取authentication.properties配置文件中的过滤器链定义        // PropertiesUtil是读取配置文件的工具类       List list =  PropertiesUtil.propertiesShiro.getKeyList();       // 必须创建一个有序的map        Map 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 filters(){        Map 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类(说明)
  1. package com.itheima.shiro.properties;import com.itheima.shiro.utils.EmptyUtil;import lombok.extern.log4j.Log4j2;/** * @Description 读取Properties的工具类 */@Log4j2public 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配置文件
  1. /static/**=anon#登录链接不拦截/login/**=anon#访问/resource/**需要有admin的角色/resource/**=roles[dev,SuperAdmin]#其他链接是需要登录的/**=authc ```
复制代码
说明:过滤器链由路径+过滤器组成,它是自上而下有序的,一个一个去匹配的。如果上面匹配失败,则不会匹配下面的内容。
自定义过滤器
  1. 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 roles = CollectionUtils.asSet(rolesArray);        for (String role : roles) {            boolean flag = subject.hasRole(role);            if (flag){                return flag;            }        }        return false;    }}
复制代码
加载自定义过滤器

在 ShiroConfig类中配置如下:
  1. /** * @Description 加载自定义过滤器 */private Map filters(){    Map 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配置文件中修改内容如下:
  1. #访问/resource/**需要有SuperAdmin或者dev的角色/resource/**=roles-or[dev,SuperAdmin]
复制代码
注解方式鉴权



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

相关推荐

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