shiro权限框架

本文详细介绍了Apache Shiro框架在权限管理方面的应用,包括基本的认证流程、自定义Realm的实现、散列加密算法的使用,以及如何进行授权操作。通过实例展示了如何配置Shiro,实现用户认证和权限控制。

1.入门案例

导入maven依赖

shiro-core:核心包
shiro-web:主要用于整合web项目
shiro-pring:主要用于整合spring框架
shiro-quartz:主要用于整合任务调度quartz
shiro-ehcache:主要用于整合ehcache缓存

pom.xml

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>   
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>

shiro.ini

[users]
admin=123456

认证代码:

    @Test
    public void loginTest(){
        //认证操作
        //加载资源文件
        Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:config/shiro.ini");
        //创建安全管理器
        SecurityManager securityManager = factory.getInstance();
        //把安全管理器设置到SecurityUtils中
        SecurityUtils.setSecurityManager(securityManager);
        //通过安全工具类创建当前Subject主体对象
        Subject subject = SecurityUtils.getSubject();
        //创建用户认证用的令牌
        UsernamePasswordToken token=new UsernamePasswordToken("admin","admin");
        //通过login方法进行认证
        try {
            subject.login(token);
            System.out.println("通过验证,可以登陆");
        } catch (AuthenticationException e) {
            System.out.println("登陆失败");
        }
        //判断认证结果
        boolean authenticated = subject.isAuthenticated();
        System.out.println("认证结果:"+authenticated);
        //退出系统
        subject.logout();
    }

认证成功:

通过验证,可以登陆
认证结果:true

认证失败:

登陆失败
认证结果:false
  • 原理分析

    1. 通过加载shiro.ini配置文件创建securityManager

    2. 通过subject.login方法提交认证,提交token封装的用户信息

    3. securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证

    4. ModularRealmAuthenticator通过调用iniRealm去ini配置文件中查询用户信息

    5. iniRealm根据输入的token从ini配置文件中查询用户信息,根据帐号查询用户信息(用户名和密码),如果查询到用户信息,就返回用户信息,查询不到,返回null

    6. ModularRealmAuthenticator根据iniRealm返回的认证信息,如果是null,抛出异常。如果不是null,说明找到了用户,将返回的用户密码和token中的密码进行对比,如果不一致则抛出异常

1.自定义realm认证

实际开发过程中,realm域主要用于数据库的交互,我们可以自定义realm类,从数据库中获取信息

自定义realm的步骤:

  1. 自定义realm类继承AuthorizingRealm

  2. 重写验证方法,连接数据库,获取数据

  3. 自定义ini配置文件

  4. 通过junit进行测试

MyRealm.java

public class MyRealm extends AuthorizingRealm {
​
    /**
     * 授权方法
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
​
    /**
     * 认证方法
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //通过token令牌获取用户账户信息
        String username = (String)authenticationToken.getPrincipal();
        //根据用户名从数据库中查询用户密码
        String password="admin";
        if(password==null||"".equals(password)){
            return null;
        }
        //把用户名密码和域的简写类封装到SimpleAuthenticationInfo
        return new SimpleAuthenticationInfo(username,password,"myRealm");
    }
}

shiro-realm.ini

[main]
authRealm=com.yfy.realm.MyRealm
securityManager.realms=$authRealm

Test.java

    @Test
    public void loginTest1(){
        //认证操作
        //加载资源文件
        Factory<SecurityManager> factory=new IniSecurityManagerFactory("classpath:config/shiro-realm.ini");
        //创建安全管理器
        SecurityManager securityManager = factory.getInstance();
        //把安全管理器设置到SecurityUtils中
        SecurityUtils.setSecurityManager(securityManager);
        //通过安全工具类创建当前Subject主体对象
        Subject subject = SecurityUtils.getSubject();
        //创建用户认证用的令牌
        UsernamePasswordToken token=new UsernamePasswordToken("admin","admin");
        //通过login方法进行认证
        try {
            subject.login(token);
            System.out.println("通过验证,可以登陆");
        } catch (AuthenticationException e) {
            System.out.println("登陆失败");
        }
        //判断认证结果
        boolean authenticated = subject.isAuthenticated();
        System.out.println("认证结果:"+authenticated);
        //退出系统
        subject.logout();
    }

3.散列加密算法

在实际开发过程中,一般密码都是以密文的形式进行保存的。shiro也对数据加密提供了支持,常见的加密算法有:md5,sha等

1.Md5Hash类 :通过带参构造器可以实现带验证的加密

2.SimpleHash类:通过带参构造器可以指定加密方式

    /**
     * md5和sha的加密算法
     */
    @Test
    public void testMd5(){
        String password="admin";
        String salt="123456";
        int times=1;
        //参数:原始密码的明文,盐值随机数,数列次数
        Md5Hash md=new Md5Hash(password,salt,times);
        System.out.println(md);
        System.out.println("---------------");
        //参数:加密方式,原始密码的明文,盐值随机数,数列次数
        SimpleHash sh=new SimpleHash("sha",password,salt,times);
        System.out.println(sh);
    }

4.散列加密算法实现步骤

  1. 数据插入到数据库的时候,先根据加密算法加密后再保存

  2. 自定义realm类,继承AuthorizingRealm抽象类

  3. 重写验证方法,根据用户名查询加密后的用户密码和盐值

  4. 编写ini配置文件,设置匹配器

Md5AutoRealm.java

public class Md5AuthRealm extends AuthorizingRealm {
​
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
    /**
     * 认证方法
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //通过token对象获取用户账号,用户自己输入的账号
        String username = token.getPrincipal().toString();
        //根据获取到的用户账号查询数据表中对应密码和盐值  加密后的密码值
        String password = "50317b958ee25a1e14449aeb95db5245";
        //密码加密到数据库时使用的盐值
        String salt = "abcd";
        //封装到SimpleAuthenticationInfo实现类对象中
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,ByteSource.Util.bytes(salt),"md5AuthRealm");
        return authenticationInfo;
    }
​
}

shiro-md5.ini

[main]
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#定义散列算法
credentialsMatcher.hashAlgorithmName=md5
#定义散列次数
credentialsMatcher.hashIterations=1
#将凭证匹配器设置到域
md5AuthRealm=com.yfy.realm.Md5AuthRealm
md5AuthRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$md5AuthRealm

5.shiro通过ini配置文件授权

shiro-perm.ini

[users]
#用户admin的密码是admin,此用户具有role1和role2两个角色
admin=admin,role1,role2
#用户test的密码是test,此用户具有role2角色
test=test,role2
​
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
​
#配置文件中的内容属于静态的资源配置定义,动态的数据配置需要从数据库中获取
#user:*代表通配符,匹配所有user操作权限

Test.java

    @Test
    public void testPerm() {
        // 加载资源文件
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro-perm.ini");
        // 创建安全管理器
        SecurityManager securityManager = factory.getInstance();
        // 把安全管理器设置到SecurityUtils
        SecurityUtils.setSecurityManager(securityManager);
        // 通过安全工具类创建subject主体对象
        Subject subject = SecurityUtils.getSubject();
        // 创建用户认证用的令牌
        UsernamePasswordToken token = new UsernamePasswordToken("test", "test");
        try {
            // 通过subject主体的login方法来进行认证
            subject.login(token);
            System.out.println("通过验证,可以登录系统");
        } catch (Exception e) {
            System.out.println("验证失败。。。。");
            e.printStackTrace();
        }
        // 判断认证结果
        boolean authenticated = subject.isAuthenticated();
        System.out.println(authenticated);
​
        // 判断当前主体对象是否属于指定的角色
        boolean hasRole = subject.hasRole("role1");
        System.out.println("当前主体是否拥有role1 :" + hasRole);
​
        // 判断是否具有自定的权限
        boolean permitted = subject.isPermitted("test");
        System.out.println("当前主体具有创建用户的权限:" + permitted);
​
        // 多角色判断
        boolean flag = subject.hasAllRoles(Arrays.asList("role1", "role2", "role3"));
        System.out.println("当前主体具有role1和role2两个角色:" + flag);
        // 多权限判断
        boolean ppflag = subject.isPermittedAll("user:create", "user:delete");
        System.out.println("ppflag:" + ppflag);
    }

输出结果:

通过验证,可以登录系统
true
admin的输入role1 :false
当前主体具有创建用户的权限:false
当前主体具有role1和role2两个角色:false
ppflag:true

6.自定义realm授权

  • 自定义realm授权的目的是为了访问数据库,操作数据库中的数据进行验证、授权判断

MyPermRealm.java

public class MyPermRealm extends AuthorizingRealm {
​
    /**
     * 授权操作
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // 获取用户名
        String username = principals.getPrimaryPrincipal().toString();
        List<String> perms = new ArrayList<String>();
        //根据用户名从数据库中获取角色信息
        String[] roles = {"role1","role2"};
        //遍历角色,根据角色获取每一个角色具有权限信息
        for(String role : roles){
            //获取权限信息,并且保存到权限列表中
            perms.add("user:create");
            perms.add("user:delete");
            perms.add("user:update");
        }
        //把权限列表封装到SimpleAuthorizationInfo对象中
        SimpleAuthorizationInfo saif = new SimpleAuthorizationInfo();
        saif.addStringPermissions(perms);
        saif.addRoles(Arrays.asList(roles));
        return saif;
    }
​
    /**
     * 认证方法
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = token.getPrincipal().toString();
        //根据用户获取用户密码
        String password = "admin"; //从数据库中获取到的密码的值
        //把用户名和密码封装到AuthenticationInfo的实现类对象中
        SimpleAuthenticationInfo shinfo = new SimpleAuthenticationInfo(username,password,"authRealm");
        return shinfo;
    }
​
}

shiro-perm-realm.ini

[main]
authRealm=com.yfy.realm.MyPermRealm
securityManager.realms=$authRealm

Test.java

    @Test
    public void testPermRealm() {
        // 加载资源文件
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:config/shiro-perm-realm.ini");
        // 创建安全管理器
        SecurityManager securityManager = factory.getInstance();
        // 把安全管理器设置到SecurityUtils
        SecurityUtils.setSecurityManager(securityManager);
        // 通过安全工具类创建subject主体对象
        Subject subject = SecurityUtils.getSubject();
        // 创建用户认证用的令牌
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin");
        try {
            // 通过subject主体的login方法来进行认证
            subject.login(token);
            System.out.println("通过验证,可以登录系统");
        } catch (Exception e) {
            System.out.println("验证失败。。。。");
            e.printStackTrace();
        }
        // 判断认证结果
        boolean authenticated = subject.isAuthenticated();
        System.out.println(authenticated);
        if (authenticated) {
            // 判断当前主体对象是否属于指定的角色
            //boolean hasRole = subject.hasRole("role1");
            //System.out.println("admin的输入role1 :" + hasRole);
​
            // 判断是否具有自定的权限
            boolean permitted = subject.isPermitted("user:create");
            System.out.println("当前主体具有创建用户的权限:" + permitted);
​
            // 多角色判断
            boolean flag = subject.hasAllRoles(Arrays.asList("role1", "role2"));
            System.out.println("当前主体具有role1和role2两个角色:" + flag);
            // 多权限判断
            boolean ppflag = subject.isPermittedAll("user:create", "user:delete");
            System.out.println("ppflag:" + ppflag);
        }
    }

输出结果:

通过验证,可以登录系统
true
当前主体具有创建用户的权限:true
当前主体具有role1和role2两个角色:true
ppflag:true

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值