zoukankan      html  css  js  c++  java
  • Shiro权限框架

    1.Shiro是什么

      Shiro是一个非常强大的、易于使用的、开源的权限框架(安全框架)。它包括了权限校验、权限授予、会话管理、安全加密等组件。

    2.为什么需要使用Shiro

      在设计RBAC(Role Based Access Control)基础系统时,需要编写大量用于权限控制的代码。如果使用Shiro就可以大大减少我们的工作量。因为Shiro已经将RBAC系统大量的代码封装好。
      如:页面的显示的HTML控件根据登录用户的权限不同而不同。使用Shiro可以轻松解决。

    3.Shiro的下载

      shiro的下载路径:http://shiro.apache.org/download.html

      Shiro包说明

      使用时根据列表的说明,下载需要的jar包即可

    包 名 Maven 坐标 说 明
     shiro-all  不推荐采用  包含Shiro的所有功能 
     shiro-core
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.3.2</version>
    </dependency>
     只要使用shiro必须的核心包,它依赖slf4j 和 commons-beanutils 以及还需要一个INI配置文件
     shiro-web  <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.3.2</version>
    </dependency>
     支持基于Web的应用
     shiro-aspectj  <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-aspectj</artifactId>
    <version>1.3.2</version>
    </dependency>
     AspectJ对Shiro AOP和注释的支持
     shiro-cas

    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-cas</artifactId>
    <version>1.3.2</version>
    </dependency>

     对cas单点登录框架的支持
     shiro-ehcache

    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.3.2</version>
    </dependency>

     对echche缓存框架的支持
     shiro-hazelcast

    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-hazelcast</artifactId>
    <version>1.3.2</version>
    </dependency>

     对hazelcast的famework缓存的支持
     shiro-features
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-features</artifactId>
    <version>1.3.2</version>
    </dependency>
     Karaf 的集成
     shiro-guice

    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-guice</artifactId>
    <version>1.3.2</version>
    </dependency>

     对谷歌的guice框架的支持(类似spring的ioc框架)
     shiro-quartz

    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-quartz</artifactId>
    <version>1.3.2</version>
    </dependency>

     对quartz定时任务调度框架的支持
     shiro-spring
    <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.3.2</version>
    </dependency>
     支持Spring框架集成。

            标红部分为常用包

    4.Shiro结构图

      

    Authentication:权限校验,每次操作校验用户是否有访问权限
    Authorization:授权,用户登录时,授予用户对应的权限
    Session Management:会话管理,用于记录用户的登录状态
    Cryptography:加密,加密算法的实现(SHA、MD5)
    web Support:对Web项目的支持,Shiro的标签!

    5.Shiro入门

      5.1 访问流程图

        登录流程:

      

    1.应用访问(如Web请求等)
    2.Shiro根据访问请求创建一个Subject对象来标识当前访问的身份。
    3.SecurityManger 加载配置文件进行身份校验(验证账号密码)
    4.身份校验通过后SecurityManger 会根据配置文件授予当前Subject对象对应的用户权限

    5.2 入门示例

    配置步骤:

    第一步:导入jar包

      

    第二步:shiro.ini配置文件
    创建一个shiro.ini配置文件,编写权限认证信息。
    注:1.shiro.ini文件名可以任意编写,但后缀必须是ini
      2.shiro.ini配置文件放在classpath根目录下

    shiro.ini文件配置规则说明:

    [main]   #用于配置SecurityManager里面的对象 
      对象名=类全限制名
      对象名.属性[.属性...] = 值 
    
    [users]   #用于配置用户名信息
       用户名= 密码, 角色1, 角色2, …, 角色N
     
    [roles]   #用于配置角色信息
       角色名= 权限1, 权限2, …, 权限N   #全部权限使用 * (星号)
       
    [urls]    #用于配置路径拦截规则

      权限命名格式建议:权限:操作:操作

      shiro.ini配置:

    ##用户信息
     [users]
     admin=123456,role_admin,role_user
     
     ##角色信息
     [roles]
     role_admin=*
     role_user=modular:to_add,modular:add

      测试:

    package com.gjs.shiro.test;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    
    public class ShiroTest {
        public static void main(String[] args) {
            //第一步:读取配置文件创建安全管理器
            IniSecurityManagerFactory factory =new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager securityManager = factory.createInstance();
            //第二步:设置SecurityUtils的安全管理器
            SecurityUtils.setSecurityManager(securityManager);
            
            //第三步:获得一个没有权限身份对象
            Subject subject = SecurityUtils.getSubject();
            
            //第四步:构建验证信息token
            UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
            
            //第四步:身份校验(验证账号密码)
            
            try {
                Subject resultSubject = securityManager.login(subject, token);
                
                System.out.println("校验通过");
                System.out.println("用户名:"+resultSubject.getPrincipal());
                System.out.println("验证授权:"+resultSubject.isPermitted("modular:add"));
                
            } catch (AuthenticationException e) {
                System.out.println("校验失败,用户名或者密码不正确");
                e.printStackTrace();
            }
            
        }
    }

    API说明:

    IniSecurityManagerFactory:作用加载ini配置文件获得SecurityManagerFactory对象
    SecurityManager:安全管理容器,就是否则整个Shiro框架授权校验对象的管理
    SecurityUtils :SecurityManager对象帮助类
    Subject:验证通过后用于存储授权信息的身份对象
    UsernamePasswordToken :用于设置校验信息的对象
    IncorrectCredentialsException :密码出错异常
    UnknownAccountException:用户名出错异常

    6.Realm的使用

      在入门示例中,用户验证信息来自于ini配置文,这样的难以符合我们实际的需求。而在实际开发中,我们的用户信息是存储在数据库里面,再从数据库里面读取出来。
    Shiro是通过Realm机制,实现将配置文件的校验用户信息存放在数据库等数据存储系统里面。

      6.1 访问流程图

      

      如图所示,我们需要在ini配置文件中配置Realm对象,再在Realm中进行权限验证以及授权

    6.2 示例:

    配置步骤:

    第一步:导入jar包

      第二步:编写Realm

    package com.gjs.shiro.realm;
    
    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.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    /**
     * 我们自定义Realm继承授权的Realm AuthorizingRealm。因为授权包括校验。
     * @author gjs
     *
     */
    public class MyRealm extends AuthorizingRealm{
        /**
         * 权限校验: 就是验证访问者(subject).是否使用有使用权限的身份,即验证账号密码。验证通过回AuthenticationInfo对象
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("权限校验");
            System.out.println("用户名:" + token.getPrincipal());
            if (token.getPrincipal().equals("admin")) {
                //参数1:用于存储用户信息,可以填充给Subject对象
                //参数2:校验的密码。注意Shiro的校验是SimpleAuthenticationInfo内部完成的。
                //参数3:Realm名字,用来标识Realm
                return new SimpleAuthenticationInfo(token.getPrincipal(), "123456", this.getName());
            }
            return null;
        }
        /**
         * 权限授予:根据通过校验的身份(subject)将查询到的权限信息封装在AuthorizationInfo里面返回
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addStringPermission("modular:add");//添加权限
            info.addRole("RoleAdmin");//添加角色
            return info;
        }
    }

      第三步:创建shiro.ini配置文件

    [main]
     ##声明Realm对象
     myRealm=com.gjs.shiro.realm.MyRealm
     ##配置securityManager的realm对象。  对象引用需要在对象名前面加上 $
     securityManager.realms=$myRealm

      第四步:编写测试类

    package com.gjs.shiro.test;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    
    public class ShiroTest {
        public static void main(String[] args) {
            //第一步:读取配置文件创建安全管理器
            IniSecurityManagerFactory factory =new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager securityManager = factory.createInstance();
            //第二步:设置SecurityUtils的安全管理器
            SecurityUtils.setSecurityManager(securityManager);
            
            //第三步:获得一个没有权限身份对象
            Subject subject = SecurityUtils.getSubject();
            
            //第四步:构建验证信息token
            UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
            
            //第四步:身份校验(验证账号密码)
            
            try {
                Subject resultSubject = securityManager.login(subject, token);
                
                System.out.println("校验通过");
                System.out.println("用户名:"+resultSubject.getPrincipal());
                System.out.println("验证授权:"+resultSubject.isPermitted("modular:add"));
                System.out.println("是否有RoleAdmin角色:"+resultSubject.hasRole("RoleAdmin"));
                
            } catch (AuthenticationException e) {
                System.out.println("校验失败,用户名或者密码不正确");
                e.printStackTrace();
            }
            
        }
    }

    6.3 加密

      在开发中我们需要对密码进行加密,而我们自己编写的加密工具类无法传递给SimpleAuthenticationInfo对象,作为密码校验。所以就要用到shiro框架自带的密码加密的功能。
      SimpleHash类:用于生成指定的Hash算法。
      HashedCredentialsMatcher类:用于让Realm校验时,校验指定的Hash算法
      ByteSource 用于给Hash算法加盐的 

      示例:

      生成md5密码

    package com.gjs.shiro.test;
    
    import org.apache.shiro.crypto.hash.Md5Hash;
    import org.apache.shiro.util.ByteSource;
    
    /**
     * 用来创建加密后的密码,参数与校验器的参数保持一致
     * @author gjs
     *
     */
    public class Md5Util {
        public static void main(String[] args) {
            ByteSource salt = ByteSource.Util.bytes("gjs");
            Md5Hash md5=new Md5Hash("123456", salt, 3);
            String password = md5.toString();
            System.out.println(password);
        }
    }

      修改ini配置文件

    [main]
     ##声明Realm对象
     myRealm=com.gjs.shiro.realm.MyRealm
     #加密的对象
     credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
     ##指定加密算法. 属性对应的是set方法
     credentialsMatcher.hashAlgorithmName=md5
     ##算法是否加盐
     credentialsMatcher.hashSalted=true
     ##加密次数
     credentialsMatcher.hashIterations=3
     ##指定加密的校验器给MyReam
     myRealm.credentialsMatcher=$credentialsMatcher
     ##配置securityManager的realm对象。  对象引用需要在对象名前面加上 $
     securityManager.realms=$myRealm

      修改Realm类:

    package com.gjs.shiro.realm;
    
    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.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    
    /**
     * 我们自定义Realm继承授权的Realm AuthorizingRealm。因为授权包括校验。
     * @author gjs
     *
     */
    public class MyRealm extends AuthorizingRealm{
        /**
         * 权限校验: 就是验证访问者(subject).是否使用有使用权限的身份,即验证账号密码。验证通过回AuthenticationInfo对象
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("权限校验");
            System.out.println("用户名:" + token.getPrincipal());
            if (token.getPrincipal().equals("admin")) {
                ByteSource salt = ByteSource.Util.bytes("gjs");
                //参数1:用于存储用户信息,可以填充给Subject对象
                //参数2:校验的密码。注意Shiro的校验是SimpleAuthenticationInfo内部完成的。
                //参数3:Realm名字,用来标识Realm
                return new SimpleAuthenticationInfo(token.getPrincipal(), "a0af233bfd499995a8c1bacc4f61c489",salt, this.getName());
            }
            return null;
        }
        /**
         * 权限授予:根据通过校验的身份(subject)将查询到的权限信息封装在AuthorizationInfo里面返回
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addStringPermission("modular:add");//添加权限
            info.addRole("RoleAdmin");//添加角色
            return info;
        }
    } 

      6.4 返回的认证信息为一个实体(JavaBean、Map)

      上面代码校验后返回的认证信息是一个字符串的用户名,而我们如果将Shiro的校验功能用到登录的逻辑里面,明显需要返回的不是一个用户名,而是用户的信息。
    用户的信息,我们需要用一个实体类来封装。可以是JavaBean或者是Map
      我们上面写的校验方法返回的SimpleAuthenticationInfo的构建方法的第一个参数就是用于指定,返回的用户认证信息的。可以将用户名修改为一个我们指定的实体类对象就可以了

    pojo实体类:

    package com.gjs.shiro.pojo;
    
    import java.util.Date;
    
    public class User {
        private int id;
        private String name;
        private String password;
        private Date createDate;
        private int status;
        private Role role;
        
        public Role getRole() {
            return role;
        }
        public void setRole(Role role) {
            this.role = role;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getPassword() {
            return password;
        }
        public void setPassword(String password) {
            this.password = password;
        }
        public Date getCreateDate() {
            return createDate;
        }
        public void setCreateDate(Date createDate) {
            this.createDate = createDate;
        }
        public int getStatus() {
            return status;
        }
        public void setStatus(int status) {
            this.status = status;
        }
        @Override
        public String toString() {
            return "User [id=" + id + ", name=" + name + ", password=" + password + ", createDate=" + createDate
                    + ", status=" + status + ", role=" + role + "]";
        }
    }
    package com.gjs.shiro.pojo;
    
    import java.util.List;
    
    public class Role {
        private int roleId;
        private String roleName;
        private List<Perm> rolePerms;
        public int getRoleId() {
            return roleId;
        }
        public void setRoleId(int roleId) {
            this.roleId = roleId;
        }
        public String getRoleName() {
            return roleName;
        }
        public void setRoleName(String roleName) {
            this.roleName = roleName;
        }
        public List<Perm> getRolePerms() {
            return rolePerms;
        }
        public void setRolePerms(List<Perm> rolePerms) {
            this.rolePerms = rolePerms;
        }
        @Override
        public String toString() {
            return "Role [roleId=" + roleId + ", roleName=" + roleName + ", rolePerms=" + rolePerms + "]";
        }
        
    }
    package com.gjs.shiro.pojo;
    
    public class Perm {
        private int permId;
        private String permName;
        private String permAction;
        private String permKey;
        public int getPermId() {
            return permId;
        }
        public void setPermId(int permId) {
            this.permId = permId;
        }
        public String getPermName() {
            return permName;
        }
        public void setPermName(String permName) {
            this.permName = permName;
        }
        public String getPermAction() {
            return permAction;
        }
        public void setPermAction(String permAction) {
            this.permAction = permAction;
        }
        public String getPermKey() {
            return permKey;
        }
        public void setPermKey(String permKey) {
            this.permKey = permKey;
        }
        @Override
        public String toString() {
            return "Perm [permId=" + permId + ", permName=" + permName + ", permAction=" + permAction + ", permKey="
                    + permKey + "]";
        }
        
    }

      修改Realm

    package com.gjs.shiro.realm;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    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.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    
    import com.gjs.shiro.pojo.Perm;
    import com.gjs.shiro.pojo.Role;
    import com.gjs.shiro.pojo.User;
    
    /**
     * 我们自定义Realm继承授权的Realm AuthorizingRealm。因为授权包括校验。
     * @author gjs
     *
     */
    public class MyRealm extends AuthorizingRealm{
        /**
         * 权限校验: 就是验证访问者(subject).是否使用有使用权限的身份,即验证账号密码。验证通过回AuthenticationInfo对象
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("权限校验");
               User user=new User(); //此处的数据应从数据库查出来
            user.setId(1);
            user.setName((String)token.getPrincipal());
            user.setStatus(0);
            user.setCreateDate(new Date());
      
            if (token.getPrincipal().equals(user.getName())) {
                ByteSource salt = ByteSource.Util.bytes("gjs");
                //参数1:用于设置认证信息,返回给调用对象的
                //参数2:校验的密码。注意Shiro的校验是SimpleAuthenticationInfo内部完成的。
                //参数3:密码的盐
                //参数4:Realm名字,用来标识Realm
                return new SimpleAuthenticationInfo(user, "a0af233bfd499995a8c1bacc4f61c489",salt, this.getName());
            }
            return null;
        }
        /**
         * 权限授予:根据通过校验的身份(subject)将查询到的权限信息封装在AuthorizationInfo里面返回
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            //获取认证信息
            User user = (User) principals.getPrimaryPrincipal();
            //将授权信息也存到当前用户对象里面(值传递)
            //角色
            Role role=new Role();
            role.setRoleId(1);
            role.setRoleName("RoleAdmin");
            user.setRole(role);
            //权限
            List<Perm> perms=new ArrayList<>();
            Perm perm1=new Perm();
            perm1.setPermId(1);
            perm1.setPermName("用户管理");
            perm1.setPermAction("/user/toUserList");
            perm1.setPermKey("user:to_edit");
               
            perms.add(perm1);
                    
            role.setRolePerms(perms);
            
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addStringPermission(user.getRole().getRolePerms().get(0).getPermKey());//添加权限
            info.addRole(user.getRole().getName());//添加角色
            return info;
        }
    }

      测试类:

    package com.gjs.shiro.test;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    
    import com.gjs.shiro.pojo.User;
    
    public class ShiroTest {
        public static void main(String[] args) {
            //第一步:读取配置文件创建安全管理器
            IniSecurityManagerFactory factory =new IniSecurityManagerFactory("classpath:shiro.ini");
            SecurityManager securityManager = factory.createInstance();
            //第二步:设置SecurityUtils的安全管理器
            SecurityUtils.setSecurityManager(securityManager);
            
            //第三步:获得一个没有权限身份对象
            Subject subject = SecurityUtils.getSubject();
            
            //第四步:构建验证信息token
            UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
            
            //第四步:身份校验(验证账号密码)
            
            try {
                Subject resultSubject = securityManager.login(subject, token);
                
                System.out.println("校验通过");
                User user=(User) resultSubject.getPrincipal();//获取认证信息
                System.out.println("用户名:"+user.getName());
                System.out.println("验证授权:"+resultSubject.isPermitted("modular:add"));
                System.out.println("是否有RoleAdmin角色:"+resultSubject.hasRole("RoleAdmin"));
                
                System.out.println("获得角色:"+user.getRole());
                System.out.println("获得第一个权限:"+user.getRole().getRolePerms().get(0));
            } catch (AuthenticationException e) {
                System.out.println("校验失败,用户名或者密码不正确");
                e.printStackTrace();
            }
            
        }
    }

    7.常用API:

    IniSecurityManagerFactory : 用于加载配置文件,创建SecurityManager对象
    SecurityManager :就是整个Shiro的控制对象
    SecurityUtils :SecurityManager 工具类,用于获得Subject对象
    Subject :身份类,存储返回的数据信息、提供了校验的权限的方法
    UsernamePasswordToken 身份信息构建类 (Token 令牌,作用就是传入校验参数)
    AuthorizingRealm 支持校验与授权的Realm
    AuthenticationInfo 校验成功返回的信息的父接口
    SimpleAuthenticationInfo 校验成功返回信息类
    Md5Hash Md5加密类
    ByteSource 字节码处理工具类,我们在构造Md5加盐时使用到。
    HashedCredentialsMatcher Md5算法校验器,用于支持Md5校验
    AuthorizationInfo 授权成功返回的信息类的父接口
    PrincipalCollection 授予是获得验证信息的类
    SimpleAuthorizationInfo 授权成功返回的信息类的实现类

  • 相关阅读:
    线性回归损失函数求解
    【线性代数】四个基本子空间
    【线性代数】如何寻找一个投影矩阵
    【hihoCoder】#1133 : 二分·二分查找之k小数
    [LeetCode解题报告] 502. IPO
    [LeetCode解题报告] 703. 数据流中的第K大元素
    【排序】堆排序
    全文检索以及Lucene的应用
    MySql优化之mycat
    MySql优化之主从复制
  • 原文地址:https://www.cnblogs.com/gaojinshun/p/11284983.html
Copyright © 2011-2022 走看看