Spring Boot 整合Shiro实现登陆认证和权限控制

我在做毕设的时候,使用了Shiro作为项目中的登陆认证和权限控制。
下面是我项目中如何实现整合shiro的学习记录。

导入shiro依赖包到pom.xml

  <!-- Shiro依赖 -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.2.2</version>
    </dependency>

配置shiro

下面给出了我项目中配置shiro的ShiroConfiguration 类中的主要代码:

package com.ciyou.edu.config.shiro.common

@Configuration
class ShiroConfiguration {

    @Autowired(required = false)
    private PermissionService permissionService

    private static final Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class)


    /**
     * ShiroFilterFactoryBean 处理拦截资源文件问题。
     * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在
     * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager
     * Filter Chain定义说明 1、一个URL可以配置多个Filter,使用逗号分隔 2、当设置多个过滤器时,全部验证通过,才视为通过
     *
     * 部分过滤器可指定参数,如perms,roles
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {

        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean()

        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters()//获取filters
        //将自定义 的权限验证失败的过滤器ShiroFilterFactoryBean注入shiroFilter
        filters.put("perms", new ShiroPermissionsFilter())

        // 必须设置SecuritManager
        shiroFilterFactoryBean.setSecurityManager(securityManager)

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login")
        //登录成功后要跳转的链接
        //shiroFilterFactoryBean.setSuccessUrl("/index")


        // 权限控制map.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>()
        // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout")
        filterChainDefinitionMap.put("/favicon.ico", "anon")
        filterChainDefinitionMap.put("/adminLogin", "anon")
        filterChainDefinitionMap.put("/teacherLogin", "anon")
        //允许访问静态资源
        filterChainDefinitionMap.put("/static/**", "anon")
        // 从数据库获取所有的权限
        List<Permission> permissionList = permissionService?.findAllPermission()
        permissionList?.each {current_Permission ->
            //规则:"roles[admin,user]", "perms[file:edit]"
            filterChainDefinitionMap?.put(current_Permission?.getUrl(),"perms[" + current_Permission?.getPermission() + "]")
            logger.info("从数据库加载的拦截器规则:资源路径: " + current_Permission?.getUrl() + " ,需要权限:" +current_Permission?.getPermission())
        }

        filterChainDefinitionMap.put("/admin/**","roles[Admin]")
        filterChainDefinitionMap.put("/teacher/**","roles[Teacher]")
        filterChainDefinitionMap.put("/student/**","roles[Student]")
        //   过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
        filterChainDefinitionMap.put("/**", "authc")
        //authc表示需要验证身份才能访问,还有一些比如anon表示不需要验证身份就能访问等。
        logger.info("拦截器链:" + filterChainDefinitionMap)

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap)
        return shiroFilterFactoryBean
    }


    //SecurityManager 是 Shiro 架构的核心,通过它来链接Realm和用户(文档中称之为Subject.)
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager()
        //设置realm.
        securityManager.setAuthenticator(modularRealmAuthenticator())
        List<Realm> realms = new ArrayList<>()
        //添加多个Realm
        realms.add(adminShiroRealm())
        realms.add(teacherShiroRealm())
        realms.add(studentShiroRealm())
        securityManager.setRealms(realms)

        return securityManager
    }




    /**
     * 系统自带的Realm管理,主要针对多realm
     * */
    @Bean
    public ModularRealmAuthenticator modularRealmAuthenticator(){
        //自己重写的ModularRealmAuthenticator
        UserModularRealmAuthenticator modularRealmAuthenticator = new UserModularRealmAuthenticator()
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy())
        return modularRealmAuthenticator
    }

    @Bean
    public AdminShiroRealm adminShiroRealm() {
        AdminShiroRealm adminShiroRealm = new AdminShiroRealm()
        adminShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher())//设置解密规则
        return adminShiroRealm
    }

    @Bean
    public StudentShiroRealm studentShiroRealm() {
        StudentShiroRealm studentShiroRealm = new StudentShiroRealm()
        studentShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher())//设置解密规则
        return studentShiroRealm
    }

    @Bean
    public TeacherShiroRealm teacherShiroRealm() {
        TeacherShiroRealm teacherShiroRealm = new TeacherShiroRealm()
        teacherShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher())//设置解密规则
        return teacherShiroRealm
    }

    //因为我们的密码是加过密的,所以,如果要Shiro验证用户身份的话,需要告诉它我们用的是md5加密的,并且是加密了两次。同时我们在自己的Realm中也通过SimpleAuthenticationInfo返回了加密时使用的盐。这样Shiro就能顺利的解密密码并验证用户名和密码是否正确了。
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher()
        hashedCredentialsMatcher.setHashAlgorithmName("md5")//散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2)//散列的次数,比如散列两次,相当于 md5(md5(""));
        return hashedCredentialsMatcher;
    }

    /**
     *  开启shiro aop注解支持.
     *  使用代理方式;所以需要开启代码支持;
     *  开启 权限注解
     *  Controller才能使用@RequiresPermissions
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor()
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager)
        return authorizationAttributeSourceAdvisor
    }


}

上述代码中的多Realm配置,可以查看我的上一篇博文进行配置:Spring Boot 集成Shiro的多realm配置

下面是上述代码的一些解释:
这里写图片描述

这里写图片描述

实体类

这里我只给出了其中的一个权限实体类:

package com.ciyou.edu.entity

class Permission implements Serializable{
    private static final long serialVersionUID = 1L
    //权限编号(自增长)
    private Integer permissionId
    //权限名称
    private String permissionName
    //权限字符串
    private String permission
    //父编号
    private Integer parentId
    //资源类型
    private Integer type
    //资源路径
    private String url

    Integer getPermissionId() {
        return permissionId
    }

    void setPermissionId(Integer permissionId) {
        this.permissionId = permissionId
    }

    String getPermissionName() {
        return permissionName
    }

    void setPermissionName(String permissionName) {
        this.permissionName = permissionName
    }

    String getPermission() {
        return permission
    }

    void setPermission(String permission) {
        this.permission = permission
    }

    Integer getParentId() {
        return parentId
    }

    void setParentId(Integer parentId) {
        this.parentId = parentId
    }

    String getUrl() {
        return url
    }

    void setUrl(String url) {
        this.url = url
    }

    Integer getType() {
        return type
    }

    void setType(Integer type) {
        this.type = type
    }


    @Override
    public String toString() {
        return "Permission{" +
                "permissionId=" + permissionId +
                ", permissionName='" + permissionName + '\'' +
                ", permission='" + permission + '\'' +
                ", parentId=" + parentId +
                ", type=" + type +
                ", url='" + url + '\'' +
                '}';
    }
}

动态修改权限

在项目中,可以对权限进行增删改操作,然而权限在shiroConfig中已经配置死了,所以我们要在我们的项目中手动更新,下面给出的是我项目中的代码:

package com.ciyou.edu.service.impl

import com.ciyou.edu.entity.Permission
import com.ciyou.edu.mapper.PermissionMapper
import com.ciyou.edu.service.ShiroService
import org.apache.shiro.spring.web.ShiroFilterFactoryBean
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver
import org.apache.shiro.web.servlet.AbstractShiroFilter
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service

@Service
class ShiroServiceImpl implements ShiroService{

    private static final Logger logger = LoggerFactory.getLogger(ShiroServiceImpl.class)
    @Autowired
    private ShiroFilterFactoryBean shiroFilterFactoryBean
    @Autowired
    private PermissionMapper permissionMapper

    /**
     * 初始化权限
     */
    public Map<String, String> loadFilterChainDefinitions() {
        // 权限控制map.从数据库获取
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>()
        filterChainDefinitionMap.put("/logout", "logout")
        filterChainDefinitionMap.put("/favicon.ico", "anon")
        filterChainDefinitionMap.put("/adminLogin", "anon")
        filterChainDefinitionMap.put("/teacherLogin", "anon")
        //允许访问静态资源
        filterChainDefinitionMap.put("/static/**", "anon")
        List<Permission> list = permissionMapper?.findAllPermission()

        for (Permission permission : list) {
            filterChainDefinitionMap.put(permission?.getUrl(),"perms["+ permission?.getPermission() +"]")
        }
        filterChainDefinitionMap.put("/admin/**","roles[Admin]")
        filterChainDefinitionMap.put("/teacher/**","roles[Teacher]")
        filterChainDefinitionMap.put("/student/**","roles[Student]")
        //   过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
        filterChainDefinitionMap.put("/**", "authc")
        return filterChainDefinitionMap
    }

    /**
     * 重新加载权限
     */
    @Override
    public void updatePermission() {

        synchronized (shiroFilterFactoryBean) {

            AbstractShiroFilter shiroFilter = null
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean?.getObject()
            } catch (Exception e) {
                logger.info("重新加载权限失败:" + e.getMessage())
                throw new RuntimeException("get ShiroFilter from shiroFilterFactoryBean error!")
            }
            PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter?.getFilterChainResolver()
            DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver?.getFilterChainManager()

            // 清空老的权限控制
            manager?.getFilterChains()?.clear()

            shiroFilterFactoryBean?.getFilterChainDefinitionMap()?.clear()
            shiroFilterFactoryBean?.setFilterChainDefinitionMap(loadFilterChainDefinitions())
            // 重新构建生成
            Map<String, String> chains = shiroFilterFactoryBean?.getFilterChainDefinitionMap()
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry?.getKey()
                String chainDefinition = entry?.getValue()?.trim()?.replace(" ", "")
                manager.createChain(url, chainDefinition)
            }

            logger.info("更新权限成功!!")
        }
    }
}

在Controller中调用
这里写图片描述

到此为止shiro的整合就基本完成了。

本文是根据我一个项目中给出的配置,具体的项目源码可以查看:CIYOU

最后,感谢几位作者的文章解惑,需要学习更过关于Spring Boot和Shiro的知识可以参考:
SpringBoot+shiro整合学习之登录认证和权限控制
SpringBoot + Shiro (一)基础工程搭建
SpringBoot + Shiro (二)身份校验和角色设置
SpringBoot + Shiro (三)权限
SpringBoot + Shiro (四)缓存&记住密码
SpringBoot + Shiro (五)验证码
springboot整合shiro-登录认证和权限管理
Spring Boot Shiro权限管理【从零开始学Spring Boot】
Spring boot 中使用Shiro

更多关于权限注解使用的可以查看:
shiro注解权限控制-5个权限注解

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值