shiro框架的使用

标签: shiro

1.什么是权限管理?
基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户可以访问而且只能访问自己被授权的资源。
权限管理包括用户身份认证和授权两部分,简称认证授权。对于需要访问控制的资源用户首先经过身份认证,认证通过后用户具有该资源的访问权限方可访问。
2.身份认证,就是判断一个用户是否为合法用户的处理过程.醉常用的简单身份认证方式是系统通过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确.对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则需要刷卡.
上边的流程图中需要理解以下关键对象:
Subject:主体
访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体;

Principal:身份信息
是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)。

credential:凭证信息
是只有主体自己知道的安全信息,如密码、证书等。
**3.授权,**即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限方可访问系统的资源,对于某些资源没有权限是无法访问的。
4.什么是Apache Shiro?
Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单,对比Spring Security,可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了。
4.1shiro的组成:
在这里插入图片描述
Authentication:身份认证/登录,验证用户是不是拥有相应的身份;
Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限;
Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储;
Web Support:Web支持,可以非常容易的集成到Web环境;
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率;
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去;
Testing:提供测试支持;
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了。

Shiro架构有三个主要概念 - Subject,SecurityManager,Realms。
在这里插入图片描述
4.2shiro的架构:
Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;
SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authorizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有1个或多个Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是JDBC实现,也可以是LDAP实现,或者内存实现等等;由用户提供;注意:Shiro不知道你的用户/权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的Realm;
SessionManager:如果写过Servlet就应该知道Session的概念,Session呢需要有人去管理它的生命周期,这个组件就是SessionManager;而Shiro并不仅仅可以用在Web环境,也可以用在如普通的JavaSE环境、EJB等环境;所有呢,Shiro就抽象了一个自己的Session来管理主体与应用之间交互的数据;可以实现分布式的会话管理;
SessionDAO:DAO大家都用过,数据访问对象,用于会话的CRUD,比如我们想把Session保存到数据库,那么可以实现自己的SessionDAO,通过如JDBC写到数据库;比如想把Session放到redis中,可以实现自己的redis SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。
在这里插入图片描述
(一)认证
4.3认证的流程
在这里插入图片描述
5.shiro的使用
5.1新建一个maven项目.先简单测试一下
5.2引入shiro的依赖

<dependency>  
        <groupId>junit</groupId>  
        <artifactId>junit</artifactId>  
        <version>4.12</version>  
    </dependency>  
    <dependency>  
        <groupId>commons-logging</groupId>  
        <artifactId>commons-logging</artifactId>  
        <version>1.1.3</version>  
    </dependency>  
    <dependency>  
        <groupId>org.apache.shiro</groupId>  
        <artifactId>shiro-core</artifactId>  
        <version>1.2.2</version>  
    </dependency>  

5.3首先准备一些用户身份/凭据(shiro.ini):模拟数据库中的数据
在这里插入图片描述
5.4新建一个测试类模拟一个登录操作

	@Test
    public void test() throws  Exception{
        //1、创建SecurityManager工厂,IniSecurityManagerFactory可以从ini文件中初始化SecurityManager环境
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
        //2、创建SecurityManager
        SecurityManager securityManager = factory.getInstance();
        //3、将securityManager设置到运行环境中
        SecurityUtils.setSecurityManager(securityManager);
        //4、在运行环境下创建Subject
        Subject subject =  SecurityUtils.getSubject();
        //5、创建token令牌,记录用户认证的身份和凭证即账号和密码
        UsernamePasswordToken token = new UsernamePasswordToken("zhangsan", "666");
        try {
            //6、登录,即身份验证  
            subject.login(token);
        } catch (AuthenticationException e) {
            //7、身份验证失败
            e.printStackTrace();
        }
        //8、用户认证状态
        Boolean isAuthenticated = subject.isAuthenticated();
        System.out.println("用户认证状态:" + isAuthenticated);//true
        //9、用户退出
        subject.logout();
        isAuthenticated = subject.isAuthenticated();
        System.out.println("用户认证状态:" + isAuthenticated);//false
    }

5.5执行流程分析
1、调用subject.login方法进行登录,其会自动委托给securityManager.login方法进行登录;
2、securityManager通过Authenticator(认证器)进行认证;
3、Authenticator的实现ModularRealmAuthenticator调用realm从ini配置文件取用户真实的账号和密码,这里使用的是IniRealm(shiro自带,相当于数据源);
4、IniRealm先根据token中的账号去ini中找该账号,如果找不到则给ModularRealmAuthenticator返回null,如果找到则匹配密码,匹配密码成功则认证通过。
5、最后调用Subject.logout进行退出操作。

6.实际开发中,密码往往都是加密的,所以这里还存在数据不安全的问题,通常会自定义一个realm继承框架的AuthorizingRealm类
测试代码和上面测试类相同,只需要改一下加载的配置文件路径即可
在这里插入图片描述

public class MyRealm extends AuthorizingRealm {
    //Realm的名称
    @Override
    public String getName() {
        return "MyRealm";
    }
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //获取用户名
        String principal = (String) token.getPrincipal();
        //根据用户名去数据库中查询查询用户对象,如果找不到就返回null
        //此处我们自己模拟数据库有一条数据 ---->zhangsan:666
        if(!"zhangsan".equals(principal)){
            //表示找不到用户,返回空认证信息
            return null;
        }
        //此处应该拿到登陆名到数据库中获取对应用户的密码
        //我们在这里就简单模拟从数据库中已经获取到密码了.
        String password = "666";//静态数据,模拟数据库中获取到的.

        //返回认证信息由父类AuthenticatingRealm进行认证
        //密码的匹配是AuthenticatingRealm进行匹配的,我们只需要返回认证信息回去即可.
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal, password,getName());
        return simpleAuthenticationInfo;
    }
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
}

7.加密数据的处理
7.1自定义Realm,模拟加密数据,数据是自己使用MD5算法加密得到的
在这里插入图片描述
7.2添加shiro-cryptography.ini配置文件

#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#散列算法
credentialsMatcher.hashAlgorithmName=md5
#散列次数
credentialsMatcher.hashIterations=1

#将凭证匹配器设置到realm
myRealm=fym.PasswordRealm
myRealm.credentialsMatcher=$credentialsMatcher
securityManager.realms=$myRealm

(二)授权
8.1流程
在这里插入图片描述
8.2授权的三种方式:
①通过写if/else授权代码块完成

Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
②通过在执行的Java方法上放置相应的注解完成

@RequiresRoles(“admin”)
public void hello() {
//有权限
}
③在JSP页面通过相应的标签完成

<shiro:hasRole name=“admin”>
<!— 有权限 —>
</shiro:hasRole>
8.3添加shiro-permission.ini配置文件

[users]
#用户zhang的密码是123,此用户具有role1和role2两个角色
zhangsan=666,role1,role2
lisi=888,role2

[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create

8.4测试类代码,和之前一样,只需要在认证之后,编写授权的代码

//8、用户认证状态
		Boolean isAuthenticated = subject.isAuthenticated();
		System.out.println("用户认证状态:" + isAuthenticated);
		//是否有某一个角色
		System.out.println("用户是否拥有一个角色:" + subject.hasRole("role2"));
		//是否有多个角色
		System.out.println("用户是否拥有多个角色:" + subject.hasAllRoles(Arrays.asList("role1", "role2")));
		//角色检查,如果没有就跑出异常
		//subject.checkRole("role1");
		//subject.checkRoles(Arrays.asList("role1", "role2"));

这里也是可以自定义realm来实现,过程比较简单,这里不做演示

版权声明:本文为weixin_41588751原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_41588751/article/details/103976670