• Shiro权限控制


    认识Shiro的整体架构,各组件的概念

    简介

    • Apache 的强大灵活的开源安全框架;
    • 认证、授权、企业会话管理、安全加密;

    Shiro与Spring Security比较

    Apache ShiroSpring Security
    简单灵活复杂笨重
    可脱离Spring不可脱离Spring
    粒度较粗粒度更细

    Shiro整体架构

    Shiro通过Security Manager提供安全服务
    Security Manager管理着其他组件的实例

    1. Authenticator(认证器):管理登录,登出
    2. Authorizer(授权器):赋予主体权限
    3. Session Manager(Session管理器):Shiro自己实现的管理机制,不借用任何容器使用Session
    4. Session Dao(提供Session的操作):主要有:增删改查。
    5. Cache Manager(缓存管理器):角色和权限数据缓存。
    6. Pauggable Realms(数据库与数据源的桥梁):shiro获取数据是通过realms来获取的。

    流程:

    1. 主体提交请求到Security Manager.
    2. Security Manager调用Authenticator进行认证。(Authenticator认证获取数据是通过realms获取的,再从数据源中获取信息)数据源信息和主体提交的信息比对。
    3. (Authorizer授权获取数据是通过realms获取的,再从数据源中获取数据)数据源信息和主体提交的信息做比对。
    4. 数据加密
      在这里插入图片描述

    Shiro认证,授权的过程

    shiro认证

    Shiro安全认证简单流程如图:

    1. 构建SecurityManager环境
    2. 主体提交认证请求
    3. SecurityManager认证
    4. Authenticator认证
    5. Realm验证(用户名,密码信息)

    注:3,4,5步认证在主体提交认证里面

    package org.example;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.realm.SimpleAccountRealm;
    import org.apache.shiro.subject.Subject;
    import org.junit.Before;
    import org.junit.Test;
    
    public class AuthenticationTest {
    
        public SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
    
        @Before
        public void addUser(){
            simpleAccountRealm.addAccount("mark", "123456");
        }
    
    
        @Test
        public void testAuthentication(){
            //1.构建SecurityManager环境
            DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
            defaultSecurityManager.setRealm(simpleAccountRealm);
    
            //2. 主体提交认证请求
            SecurityUtils.setSecurityManager(defaultSecurityManager);
            Subject subject = SecurityUtils.getSubject();
    
            UsernamePasswordToken token = new UsernamePasswordToken("mark", "123456");
            subject.login(token);
    
            System.out.println("isAuthenticated:" + subject.isAuthenticated());
    
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    Shiro授权

    Shiro授权
    与之前Shiro认证的步骤一样。
    只不过在Realm 的SimpleAccountRealm中可以添加addUser的时候,可以添加入多个角色(即可变数组的形式)
    同样进行授权验证即检验该登录用户是否具备该角色的时候,使用:
    subject.checkRoles(可变参数)的形式检验。
    必须在登录的情况下,其他步骤与认证相同。

    package org.example;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.realm.SimpleAccountRealm;
    import org.apache.shiro.subject.Subject;
    import org.junit.Before;
    import org.junit.Test;
    
    public class AuthenticationTest {
    
        public SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
    
        @Before
        public void addUser(){
            simpleAccountRealm.addAccount("mark", "123456", "admin", "user");
        }
    
    
        @Test
        public void testAuthentication(){
            //1.构建SecurityManager环境
            DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
            defaultSecurityManager.setRealm(simpleAccountRealm);
    
            //2. 主体提交认证请求
            SecurityUtils.setSecurityManager(defaultSecurityManager);
            Subject subject = SecurityUtils.getSubject();
    
            UsernamePasswordToken token = new UsernamePasswordToken("mark", "123456");
            subject.login(token);
    
            System.out.println("isAuthenticated:" + subject.isAuthenticated());
    
            subject.checkRoles("admin", "user");
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    Shiro自定义的Realm,Filter

    IniRealm配置文件

    IniRealm就是把 realm 的信息以.ini的配置文件形式保存,
    其他的和认证授权没啥区别

    package org.example;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.realm.Realm;
    import org.apache.shiro.realm.text.IniRealm;
    import org.apache.shiro.subject.Subject;
    import org.junit.Test;
    
    public class IniRealmTest {
    
    
    
        @Test
        public void testAuthentication(){
    
            //获取realm配置文件
            IniRealm iniRealm = new IniRealm("classpath:user.ini");
    
            //1.构建SecurityManager环境
            DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
            defaultSecurityManager.setRealm(iniRealm);
    
    
            //2. 主体提交认证请求
            SecurityUtils.setSecurityManager(defaultSecurityManager);
            Subject subject = SecurityUtils.getSubject();
    
            UsernamePasswordToken token = new UsernamePasswordToken("mark", "123456");
            subject.login(token);
    
            System.out.println("isAuthenticated:" + subject.isAuthenticated());
    
            subject.checkRoles("admin", "user");
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    ini配置文件

    [users]
    mark=123456,admin,user
    [roles]
    admin=user:delete,user:update
    
    • 1
    • 2
    • 3
    • 4

    JdbcRealm

    JdbcRealm就是把 realm 的信息从数据库里去查找,默认有一些JdbcRealm自带有一些默认的SQL。realm的信息主要有用户信息users角色信息user_roles角色的权限信息roles_permissions
    验证信息的SQL也可以自定义编写
    注意:权限校验要在jdbcRealm里开启权限

    package org.example;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.realm.jdbc.JdbcRealm;
    import org.apache.shiro.realm.text.IniRealm;
    import org.apache.shiro.subject.Subject;
    import org.junit.Test;
    
    public class JdbcRealmTest {
    
        DruidDataSource dataSource = new DruidDataSource();
    
        {
            dataSource.setUrl("jdbc:mysql://localhost:3306/shiro_demo?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true");
            dataSource.setUsername("root");
            dataSource.setPassword("root");
        }
    
        @Test
        public void testAuthentication(){
    
            JdbcRealm jdbcRealm = new JdbcRealm();
            jdbcRealm.setDataSource(dataSource);
            jdbcRealm.setPermissionsLookupEnabled(true);
    
            //自定义用户信息验证SQL
            String userSql = "select password from users_test where username= ?";
            jdbcRealm.setAuthenticationQuery(userSql);
    
            //自定义角色验证SQL
            String roleSql = "select role_name from user_roles_test where username = ?";
            jdbcRealm.setUserRolesQuery(roleSql);
    
            //自定义角色权限验证SQL
            String permissionSql = "select permission from roles_permissions_test where role_name =?";
            jdbcRealm.setPermissionsQuery(permissionSql);
    
            //1.构建SecurityManager环境
            DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
            defaultSecurityManager.setRealm(jdbcRealm);
    
    
            //2. 主体提交认证请求
            SecurityUtils.setSecurityManager(defaultSecurityManager);
            Subject subject = SecurityUtils.getSubject();
    
    //        UsernamePasswordToken token = new UsernamePasswordToken("mark", "123456");
            UsernamePasswordToken token = new UsernamePasswordToken("rose", "111222");
            subject.login(token);
    
            System.out.println("isAuthenticated:" + subject.isAuthenticated());
    
    //        subject.checkRoles("admin");
            subject.checkRoles("user");
    
    //        subject.checkPermission("user:select");
            subject.checkPermission("user:update");
    
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    自定义Realm

    自定义的realm,要注意以下几个步骤:

    1. 首先继承并实现类AuthorizingRealm的方法。其中方法doGetAuthenticationInfo主要做认证操作,即可以通过用户名,查询出相应的密码,然后将用户名与密码一同返回,shiro会自动根据传入的用户名和密码与此realm返回的用户名和密码做比对,返回你想要的结果。
      同理,doGetAuthorizationInfo主要是用来做角色和权限的验证,也是通过用户名,从数据库中查找到相应的角色或者权限的数据并返回一个simple的授权类,授权系统会根据传入的参数与返回的结果集对比,存在返回TRUE,不存在则抛出异常。
    2. 两者方法只能获取用户名称,通过用户名称连接数据库获取其他信息。这里只是做数据准备操作,并不做判断是否传入的值与其相符的操作,此操作在此之后进行
    3. 根据两者的返回对象分别返回simple类型的对象。认证的对象需要将你获取的用户名和密码放到改造方法中。授权对象需要set到相应的方法中。
    package org.example.config;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthenticatingRealm;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    import java.util.HashMap;
    import java.util.HashSet;
    import java.util.Map;
    import java.util.Set;
    
    public class CustomRealm extends AuthorizingRealm {
    
        Map<String, String> usermap = new HashMap<String, String>();
    
        {
            usermap.put("mark", "123456");
    
            super.setName("customRealm");
        }
    
    
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            String username = (String) principalCollection.getPrimaryPrincipal();
            //从数据库缓存中获取角色数据
            Set<String> roles = getRolesByUserName(username);
            
            //获取用户权限
            Set<String> permissions = getPermissionsByUserName(username);
    
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            simpleAuthorizationInfo.setRoles(roles);
            simpleAuthorizationInfo.setStringPermissions(permissions);
    
            return simpleAuthorizationInfo;
        }
        
        private Set<String> getPermissionsByUserName(String userName) {
            Set<String> sets = new HashSet<String>();
            sets.add("user:delete");
            sets.add("user:add");
            return sets;
        }
    
        private Set<String> getRolesByUserName(String username) {
            Set<String> sets = new HashSet<String>();
            sets.add("admin");
            sets.add("user");
            return sets;
        }
    
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //1.从主体传过来的认证信息中,获得用户名
            String userName = (String) authenticationToken.getPrincipal();
    
            //2.通过用户名从数据库中获取凭证
            String password = getPasswordUsername(userName);
            if (password == null) {
                return null;
            }
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("Mark", password, "customRealm");
    
            return authenticationInfo;
        }
    
        //模拟数据库查询凭证
        public String getPasswordUsername(String username){
            return usermap.get(username);
        }
    
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    shiro加密

    Shiro散列配置

    • HashedCredentialsMatcher
      通过HashedCredentialsMaster的方式加密,创建对象,并设置加密次数以及加密名称,将此加密对象设置到Realm的setCredentialsmaster的方法中,即该Realm采用此密码加密的方式。
      后台的密码采用MD5的hash码值来存储。
      在这里插入图片描述

    • 盐的使用
      加盐的意思就是在密码中拼接其他字符串,组成一个新的密码,然后对这个密码进行md5加密。同时,可以在自定义realm里面的doGetAuthenticationInfo方法进行设置盐进行解密
      在这里插入图片描述

    在这里插入图片描述

    Shiro Session管理

    Shiro 缓存管理

    Shiro集成Spring

    shiro集成spring

    1,引入jar包

    <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.3.8</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.3.8</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.8.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.8.0</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-web</artifactId>
                <version>1.8.0</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2,新建一个xml文件配置shiro主体

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager" />
            <property name="loginUrl" value="login.html"/>
            <property name="unauthorizedUrl" value="403.html" />
            <property name="filterChainDefinitions" >
                <value>
                    /login.html = anon
                    /subLogin = anon
                    /* = authc
                </value>
            </property>
    
        </bean>
    
    <!--    创建SecurityManager对象-->
        <bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
            <property name="realm" ref="realm" />
        </bean>
    
        <bean id="realm" class="org.example.shiro.realm.CustomRealm" >
            <property name="credentialsMatcher" ref="credentialsMatcher"/>
        </bean>
    
        <!--设置加密的算法-->
        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"
              id="credentialsMatcher">
            <property name="hashAlgorithmName" value="md5"/>
            <property name="hashIterations" value="1"/>
        </bean>
    
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    通过注解的方式配置角色权限

    1. 开启aop设置TRUE;
    2. 添加LifecycleBeanPostProcessor;
    3. 添加AuthorizationAttributeSourceAdvisor
    <aop:config proxy-target-class="true" />
        <bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
            <property name="securityManager" ref="securityManager" />
        </bean>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 接口上面配置注解
        @RequestMapping(value = "/test", method = RequestMethod.GET)
        @ResponseBody
        @RequiresRoles("admin")
        public String testRoles() {
            return "access";
        }
    
        @RequestMapping(value = "/test", method = RequestMethod.GET)
        @ResponseBody
        @RequiresPermissions("add")
        public String testPermissions() {
            return "access";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    shiro过滤器

    shiro内置过滤器
    角色相关:

    • anon 代表无需权限
    • authBasic
    • authc 代表需要认证才能访问
    • user 代表需要存在用户对象才能访问
    • logout 登录退出才能访问

    权限相关:

    • perms 拥有权限才能被访问
    • roles 拥有角色才能被访问
    • ssl
    • port 相应端口号才能访问
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
            <property name="securityManager" ref="securityManager" />
            <property name="loginUrl" value="login.html"/>
            <property name="unauthorizedUrl" value="403.html" />
            <property name="filterChainDefinitions" >
                <value>
                    /login.html = anon
                    /subLogin = anon
                    /testRole = roles["admin"]
                    /testRole1 = roles["admin","admin1"]
                    /testPerms = perms["user:delete"]
                    /testPerms1 = perms["user:delete","user:update"]
                    /* = authc
                </value>
            </property>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  • 相关阅读:
    C++ 学习(20)STL - map容器
    理解JavaScript事件循环机制
    前端Vue返回顶部[功能]和底部四个角[样式](代源码和详图)
    浅谈在操控器类中,为何要通过osgGA::CameraManipulator的逆矩阵改变视点位置
    Java设计模式(二)创建型设计模式
    一次 G1 堆大小不均问题的排查及解决
    如何设计一套单点登录系统 ?
    m基于matlab的光通信的信道估计,均衡,抑制papr误码率仿真,对比ZF,RLS,MMSE三种算法
    连词分为并列连词和从属连词
    qt example plugandpaint 插件 动态库 pnp_extrafiltersd.dll无法加载问题
  • 原文地址:https://blog.csdn.net/u013303534/article/details/124968689