Springboot集成shiro--登录拦截/权限控制

一、在pom文件中添加依赖

 <!--Shiro核心框架 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!-- Shiro使用Spring框架 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!-- Shiro使用EhCache缓存框架 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!-- thymeleaf模板引擎和shiro框架的整合 -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>${thymeleaf.extras.shiro.version}</version>
        </dependency>

二、配置类shiroConfig--登录认证拦截

1.创建realm配置realm

2.配置安全管理器DefaultWebSecurityManager----核心

3.配置请求过滤器ShiroFilterFactoryBean

 @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager)
    {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // Shiro的核心安全接口,这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 身份认证失败,则跳转到登录页面的配置
        shiroFilterFactoryBean.setLoginUrl("/static/login.html");
        // 权限认证失败,则跳转到指定页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/nopermissioin");
        // Shiro连接约束配置,即过滤链的定义
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 对静态资源设置匿名访问
        // 不需要拦截的访问
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/favicon.ico**", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
​
        // 退出 logout地址,shiro去清除session
        filterChainDefinitionMap.put("/logout", "logout");
        // 所有请求需要认证
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

4.登录控制器,使用subject登录

   Subject subject= SecurityUtils.getSubject();
        UsernamePasswordToken token= new UsernamePasswordToken(username, password);
        subject.login(token);

5.shiro内置过滤器介绍

anon: 匿名拦截器,即不需要登录即可访问;一般用于静态资源过滤;示例“/static/**=anon”

authc: 表示需要认证(登录)才能使用;示例“/**=authc”

authcBasic:Basic HTTP身份验证拦截器

roles: 角色授权拦截器,验证用户是否拥有资源角色;示例“/admin/**=roles[admin]”

perms: 权限授权拦截器,验证用户是否拥有资源权限;示例“/user/create=perms["user:create"]”

user: 用户拦截器,用户已经身份验证/记住我登录的都可;示例“/index=user”

logout: 退出拦截器,主要属性:redirectUrl:退出成功后重定向的地址(/);示例“/logout=logout”

port: 端口拦截器,主要属性:port(80):可以通过的端口;示例“/test= port[80]”,如果用户访问该页面是非80,将自动将请求端口改为80并重定向到该80端口,其他路径/参数等都一样

rest: rest风格拦截器;

ssl: SSL拦截器,只有请求协议是https才能通过;否则自动跳转会https端口(443);其他和port拦截器一样;

注:
anon,authcBasic,auchc,user是认证过滤器,
perms,roles,ssl,rest,port是授权过滤器

三、权限控制(鉴权

1.注解式

①在需要权限的控制器方法上贴注解
  • @RequiresPermissions("权限表达式") ==> 需要权限控制

  • @RequiresRoles() ==> 需要角色控制

②在配置文件中配置两个bean
     /**
     * 开启Shiro注解通知器
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            DefaultWebSecurityManager securityManager)
    {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * 设置支持CGlib代理
     * 详情看DefaultAopProxyFactory#createAopProxy
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
③在realm中完善鉴权逻辑
//授权相关
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    Employee employee = (Employee)principals.getPrimaryPrincipal();
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    //根据用户的id查询该用户拥有的角色编码
    List<Role> roles = roleService.queryByEmployeeId(employee.getId());
    for(Role role:roles){
        info.addRole(role.getSn());
    }
    //根据用户的id查询该用户拥有的权限表达式
    List<String> permissions = permissionService.queryByEmployeeId(employee.getId());
    info.addStringPermissions(permissions);
    return info;
}
注:可以添加统一异常处理--针对用户没有权限
@ControllerAdvice
public class CommonControllerAdvice {
    @ExceptionHandler(AuthorizationException.class)
    public String exceptionHandler(AuthorizationException e){
        e.printStackTrace();
        return "/nopermission";
    }
}
注--超级管理员
        
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection){
        Employee employee = (Employee) principalCollection.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        if (employee.isAdmin()) {//是管理员--权限可以使用通配符
            info.addStringPermission("*:*");
            //角色--不能使用通配符
            List<Role> roles = roleService.selAll();
            for (Role role : roles) {
                info.addRole(role.getSn());
            }else{
                 //余下逻辑与普通鉴权相同
            }
           return info;
        }

2.编程式--以查询为例

@RequestMapping("/list")
public String list(Model model, QueryObject qo) {
    //获取subject主体对象
    Subject subject = SecurityUtils.getSubject();
    PageInfo<Department> pageInfo = null;
    //判断此subject是否有这个权限--我猜测这个subject就是从数据库查出来的用户信息,根据id和关联表查出的权限
    if(subject.isPermitted("department:list")){
        //有权限执行代码
        pageInfo = departmentService.query(qo);
    }else{
        //没有权限返回空页面
        pageInfo = new PageInfo<>(Collections.EMPTY_LIST);
    }
    model.addAttribute("pageInfo", pageInfo);
    return "department/list";
}

3.标签式--需要配置Shiro集成ThymeLeaf标签支持(了解)

①配置文件中添加依赖
/**
 * thymeleaf模板引擎和shiro框架的整合
 */
@Bean
public ShiroDialect shiroDialect()
{
    return new ShiroDialect();
}
②页面中添加头
<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
③使用
<a href="#" class="btn btn-success btn-input" style="margin: 10px" shiro:hasPermission="department:saveOrUpdate">
    <span class="glyphicon glyphicon-plus"></span> 添加
</a>

四、其他集成

4.1 ehchche--缓存

1.在resource目录下新建ehcache/ehcache-shiro.xml文件,内容如下:
  • <?xml version="1.0" encoding="UTF-8"?>
    <ehcache>
        <defaultCache
                maxElementsInMemory="1000"
                eternal="false"
                timeToIdleSeconds="600"
                timeToLiveSeconds="600"
                memoryStoreEvictionPolicy="LRU">
        </defaultCache>
    </ehcache>
    • 配置属性说明

参数说明
maxElementsInMemory缓存对象最大个数
eternal对象是否永久有效,一但设置了,timeout 将不起作用。
timeToIdleSeconds对象空闲时间,指对象在多长时间没有被访问就会失效(单位:秒)。仅当 eternal=false 对象不是永久有效时使用,可选属性,默认值是 0,也就是可闲置时间无穷大。
timeToLiveSeconds对象存活时间,指对象从创建到失效所需要的时间(单位:秒)。仅当 eternal=false 对象不是永久有效时使用,默认是 0,也就是对象存活时间无穷大。
memoryStoreEvictionPolicy当达到 maxElementsInMemory 限制时,Ehcache 将会根据指定的策略去清理内存。

缓存淘汰策略:

策略说明
LRU默认,最近最少使用,距离现在最久没有使用的元素将被清出缓存
FIFO先进先出, 如果一个数据最先进入缓存中,则应该最早淘汰掉
LFU较少使用,意思是一直以来最少被使用的,缓存的元素有一个hit 属性(命中率),hit 值最小的将会被清出缓存

2.添加配置
@Bean
public EhCacheManager getEhCacheManager()
{
    EhCacheManager em = new EhCacheManager();
    em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml");
    return em;

}
/**
 * 自定义Realm
 */
@Bean
public EmployeeRealm userRealm(EhCacheManager cacheManager)
{
    EmployeeRealm employeeRealm = new EmployeeRealm();
    employeeRealm.setCacheManager(cacheManager);
    return employeeRealm;
}
3.使用自定义的配置
@Bean
	//传递的参数名要与自定义realm的bean名一致-userRealm
    public DefaultWebSecurityManager securityManager(EmployeeRealm userRealm,DefaultWebSessionManager sessionManager){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //添加realm对象
        securityManager.setRealm(userRealm);
        //添加session管理器对象
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

4.2加盐加密

1.在数据库和实体类中分别添加salt字段
2.修改对应的mapper映射文件
3.改realm
/**
*	在返回值中添加第三个参数 ByteSource.Util.bytes(employee.getSalt())
*/
@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String username = (String) authenticationToken.getPrincipal();
        Employee employee = employeeService.selUserByUsername(username);
        if (employee == null) {
            return null;
        }
        return new SimpleAuthenticationInfo(employee, employee.getPassword(),
                ByteSource.Util.bytes(employee.getSalt()), getName());
    }
4.改配置类
  • 添加凭证匹配器

  • 	@Bean
        public HashedCredentialsMatcher hashedCredentialsMatcher(){
            HashedCredentialsMatcher matcher=new HashedCredentialsMatcher("md5");
            matcher.setHashIterations(3);
            return matcher;
        }

  • 将凭证匹配器设置到realm中

  • 	@Bean
        public EmployeeRealm userRealm(EhCacheManager cacheManager,
                                       HashedCredentialsMatcher hashedCredentialsMatcher)
        {
            EmployeeRealm employeeRealm = new EmployeeRealm();
            employeeRealm.setCacheManager(cacheManager);
            //这里
            employeeRealm.setCredentialsMatcher(hashedCredentialsMatcher);
            return employeeRealm;
        }

5.注册功能修改--service
//添加用户的service方法
	@Override
    public void add(Employee employee, Long[] roleIds) {
    	//生成随机盐
        String salt = UUID.randomUUID().toString().substring(0, 4);
        //生成加密的密码
        Md5Hash pwd=new Md5Hash(employee.getPassword(), salt,3);
        //将盐和密码都设置给对象
        employee.setPassword(pwd.toString());
        employee.setSalt(salt);
        //最后进行添加
        employeeMapper.insert(employee);
        
        //向关联表批量插入
        if (roleIds != null && roleIds.length > 0 && !employee.isAdmin())//合理化校验
        {
            employeeMapper.insertRelationBatch(employee.getId(), roleIds);
        }
    }

五、关于重定向出现404--sessionId

1.在配置类中添加session管理器

/**
 * session管理器
 */
@Bean
public DefaultWebSessionManager sessionManager() {
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    sessionManager.setSessionIdUrlRewritingEnabled(false);
    return sessionManager;
}

2.在安全管理器中添加

	//安全管理器对象
    @Bean
    public DefaultWebSecurityManager securityManager(EmployeeRealm userRealm,DefaultWebSessionManager sessionManager){
        DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
        //添加realm对象
        securityManager.setRealm(userRealm);
        //添加session管理器对象
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值