Apache Shiro快速入门教程之初识Shiro(一)

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 Apache Shiro快速入门教程之初识Shiro(一),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

Shiro简介

1.什么是Shiro?

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。对于任何一个应用程序,Shiro 都可以提供全面的安全管理服务。

Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。

2.Shiro的功能模块

在这里插入图片描述

Authentication:身份认证/登录,验证用户是不是拥有相应的身份。

Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情。

Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境的,也可以是如Web环境的。

Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。

Web Support:Shiro 的 web 支持的 API 能够轻松地帮助保护 Web 应用程序。

Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率。

Concurrency:Apache Shiro 利用它的并发特性来支持多线程应用程序。

Testing:测试支持的存在来帮助你编写单元测试和集成测试,并确保你的能
够如预期的一样安全。

"Run As":一个允许用户假设为另一个用户身份(如果允许)的功能。

"Remember Me":记住我。

3.Shiro的外部结构

在这里插入图片描述
Shiro就是一个最简单的应用:
1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;
2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。

4.Shiro的内部结构

在这里插入图片描述

Subject:主体,可以看到主体可以是任何可以与应用交互的“用户”;

SecurityManager:相当于SpringMVC中的DispatcherServlet或者Struts2中的FilterDispatcher;是Shiro的心
脏;所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、且负责进行认证和授权、及会
话、缓存的管理。

Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得Shiro默认的不好,可以自定义实
现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;

Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的
哪些功能;

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放到Memcached中,可以实现自己的
Memcached SessionDAO;另外SessionDAO中可以使用Cache进行缓存,以提高性能;

CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到
缓存中后可以提高访问的性能

Cryptography:密码模块,Shiro提高了一些常见的加密组件用于如密码加密/解密的。

添加依赖环境

方式一

		<dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-quartz</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
	      <groupId>junit</groupId>
	      <artifactId>junit</artifactId>
	      <version>4.12</version>
	      <scope>test</scope>
    </dependency>

方式二

	<dependency>
          <groupId>org.apache.shiro</groupId>
          <artifactId>shiro-all</artifactId>
          <version>1.2.3</version>
     </dependency>
      <dependency>
	      <groupId>junit</groupId>
	      <artifactId>junit</artifactId>
	      <version>4.12</version>
	      <scope>test</scope>
    </dependency>

Shiro认证

认证:身份认证/登录,验证用户是不是拥有相应的身份。基于shiro的认证,是通过subject的login方法完成用户认证工作的

一、初始化配置文件

创建shiro.ini配置文件,通过 [users] 指定了主体,将用户名和密码配置在shiro.ini配置文件中,以此模拟从数据库查询出的用户。

#数据格式  用户名=密码
[users]
admin=admin123

二、Shiro认证测试

@Test
    public void testLoginAndLogout() {

        //获取SecurityManager工厂,使用Ini配置文件初始化SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

        //通过工厂创建SecurityManager实例
       SecurityManager securityManager = factory.getInstance();

        //securityManager绑定给SecurityUtils
        SecurityUtils.setSecurityManager(securityManager);

        // 创建Subject实例,该实例认证要使用上边创建的securityManager进行
        Subject subject = SecurityUtils.getSubject();

        // 构造主体登录的token凭证(即用户名/密码)
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin123");
        try {
            //登录,即身份验证
            subject.login(token);
        } catch (AuthenticationException e) {
            // 身份验证失败
            e.printStackTrace();
        }

        // 用户认证状态
        Boolean isAuthenticated = subject.isAuthenticated();
        System.out.println("用户认证状态:" + isAuthenticated);

        // 用户退出
        subject.logout();
        isAuthenticated = subject.isAuthenticated();
        System.out.println("用户认证状态:" + isAuthenticated);

    }

Shiro授权

授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限

一、初始化配置文件

创建存放权限的配置文件shiro-permission.ini

#用户、角色、权限的配置规则如下:

[users]
#模拟从数据库查询的用户
#数据格式  用户名=密码,角色1,角色2

#用户admin的密码是admin123,具有role1和role2两个角色
admin=admin123,role1,role2

#用户lisi的密码是lisi123,具有role1角色
lisi=lisi123,role1

[roles]
#模拟从数据库查询的角色和权限列表
#数据格式  角色名=权限1,权限2

#角色role1对资源user拥有add,select权限
role1=user:add,user:select

#角色role2对资源user拥有create、update权限
role2=user:create,user:update

二、Shiro授权测试

    @Test
    public void testLoginAndLogout() {

        //获取SecurityManager工厂,使用Ini配置文件初始化SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-permission.ini");

        //通过工厂创建SecurityManager实例
       SecurityManager securityManager = factory.getInstance();

        //securityManager绑定给SecurityUtils
        SecurityUtils.setSecurityManager(securityManager);

        // 创建Subject实例,该实例认证要使用上边创建的securityManager进行
        Subject subject = SecurityUtils.getSubject();

        // 创建token令牌,记录用户认证的身份和凭证
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin123");
        try {
            //登录,即身份验证
            subject.login(token);
        } catch (AuthenticationException e) {
            // 身份验证失败
            e.printStackTrace();
        }

        // 用户认证状态
        Boolean isAuthenticated = subject.isAuthenticated();
        System.out.println("用户认证状态:" + isAuthenticated);

        // 基于角色的授权:
        // 是否有某一个角色
        System.out.println("用户是否拥有一个角色:" + subject.hasRole("role1"));
        // 是否有多个角色
        System.out.println("用户是否拥有多个角色:" + subject.hasAllRoles(Arrays.asList("role1", "role2")));

        //用户授权检测,失败抛出异常
        //subject.checkRole("role1");
        //subject.checkRoles(Arrays.asList("role1", "role2"));


        // 基于资源授权:
        System.out.println("是否拥有某一个权限:" + subject.isPermitted("user_add"));
        System.out.println("是否拥有多个权限:" + subject.isPermittedAll("user_create","user_delete"));

        //检查权限,失败抛出异常
       // subject.checkPermission("sys:user:delete");
       // subject.checkPermissions("user:create:1","user:delete");
        
        // 用户退出
        subject.logout();
        isAuthenticated = subject.isAuthenticated();
        System.out.println("用户认证状态:" + isAuthenticated);
    }
}

自定义Realm

Realm域:Shiro从Realm获取安全数据(如用户、角色、权限),就是说SecurityManager要验证用户身份,那么它需要从Realm获取相应的用户进行比较以确定用户身份是否合法;也需要从Realm得到用户相应的角色/权限进行验证用户是否能进行操作;可以把Realm看成DataSource,即安全数据源

一、初始化配置文件

单Realm域

[main]
	#自定义 realm
	ShiroRealm=cn.ybzy.shiro.security.ShiroRealm
	
	#将realm设置到securityManager
	securityManager.realms=$ShiroRealm

多 Realm 配置

[main]
#自定义 realm
ShiroRealm=cn.ybzy.shiro.security.ShiroRealm1
ShiroRealm=cn.ybzy.shiro.security.ShiroRealm2

#将realm设置到securityManager
securityManager.realms=$ShiroRealm1,$ShiroRealm2

多realm域时securityManager 会按照 realms 指定的顺序进行身份认证

二、自定义Realm

默认使用的是IniRealm域
在这里插入图片描述

public class ShiroRealm extends AuthorizingRealm {

    @Override
    public String getName() {
        return "ShiroRealm";
    }

    //登录认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken token) throws AuthenticationException {

        //从token中 获取用户身份信息
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());


        //用username与password 与数据库进行用户账号/密码校验 校验失败抛出异常
     
        if(!username.equals("123")){
            throw new UnknownAccountException("用户名或密码错误!");
        }

        //返回认证信息由父类AuthenticatingRealm进行认证
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, getName());

        return simpleAuthenticationInfo;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
    //从principals获取已认证用户的信息
    String username = (String) principalCollection.getPrimaryPrincipal();
		
		//根据用户名去数据库查询该用户对应的角色以及权限
		
		//角色授权,模拟数据库中查询的角色
        Set<String> roles = new HashSet<>();
        roles.add("role1");
        roles.add("role2");

        //资源授权,模拟数据库中查询的权限
        Set<String> permission = new HashSet<>();
        permission.add("user_add");
        permission.add("user_select");

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //将查询的角色、权限数据保存到simpleAuthorizationInfo
        info.setStringPermissions(permission);
        info.setRoles(roles);
        return info;
    }

}

三、自定义Realm测试

    @Test
    public void testLoginAndLogout() {

        //获取SecurityManager工厂,使用Ini配置文件初始化SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini");

        //通过工厂创建SecurityManager实例
       SecurityManager securityManager = factory.getInstance();

        //securityManager绑定给SecurityUtils
        SecurityUtils.setSecurityManager(securityManager);

        // 创建Subject实例,该实例认证要使用上边创建的securityManager进行
        Subject subject = SecurityUtils.getSubject();

        // 创建token令牌,记录用户认证的身份和凭证
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "admin123");
        try {
            //登录,即身份验证
            subject.login(token);
        } catch (AuthenticationException e) {
            // 身份验证失败
            e.printStackTrace();
        }

        // 用户认证状态
        Boolean isAuthenticated = subject.isAuthenticated();
        System.out.println("用户认证状态:" + isAuthenticated);

        // 基于角色的授权:
        // 是否有某一个角色
        System.out.println("用户是否拥有一个角色:" + subject.hasRole("role1"));
        // 是否有多个角色
        System.out.println("用户是否拥有多个角色:" + subject.hasAllRoles(Arrays.asList("role1", "role2")));

        //用户授权检测,失败抛出异常
        //subject.checkRole("role1");
        //subject.checkRoles(Arrays.asList("role1", "role2"));


        // 基于资源授权:
        System.out.println("是否拥有某一个权限:" + subject.isPermitted("user_add"));
        System.out.println("是否拥有多个权限:" + subject.isPermittedAll("user_create","user_delete"));

        //检查权限,失败抛出异常
       // subject.checkPermission("sys:user:delete");
       // subject.checkPermissions("user:create:1","user:delete");
        
        // 用户退出
        subject.logout();
        isAuthenticated = subject.isAuthenticated();
        System.out.println("用户认证状态:" + isAuthenticated);
    }
}

认证与授权的执行流程

(1) 认证流程

在这里插入图片描述

1. 首先调用Subject.login(token)进行登录,其会自动委托给Security Manager,调用之前必须通过SecurityUtils. setSecurityManager()设置;

2. SecurityManager负责真正的身份验证逻辑;它会委托给Authenticator进行身份验证;

3. Authenticator才是真正的身份验证者,Shiro API中核心的身份认证入口点,此处可以自定义插入自己的实
现;

4. Authenticator可能会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModularRealmAuthenticator会调用AuthenticationStrategy进行多Realm身份验证;

5. Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有返回/抛出异常表示身份
验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。

(2) 授权流程

在这里插入图片描述

1. 首先调用Subject.isPermitted/hasRole接口,其会委托给SecurityManager,而SecurityManager接着会委托
给Authorizer;

2. Authorizer是真正的授权者,如果我们调用如isPermitted(“user:view”),其首先会通过PermissionResolver
把字符串转换成相应的Permission实例;

3. 在进行授权之前,其会调用相应的Realm获取Subject相应的角色/权限用于匹配传入的角色/权限;

4. Authorizer会判断Realm的角色/权限是否和传入的匹配,如果有多个Realm,会委托给ModularRealmAuthorizer进行循环判断,如果匹配如isPermitted/hasRole会返回true,否则返回false表示
授权失败

授权自定义 realm

public class ShiroRealm extends AuthorizingRealm {

    @Override
    public String getName() {
        return "ShiroRealm";
    }

    //登录认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {

        //从token中 获取用户身份信息
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());


        //用username 与 password 进行用户账号/密码校验 校验失败抛出异常
        if(!username.equals("123")){
            throw new UnknownAccountException("用户名或密码错误!");
        }

        //返回认证信息由父类AuthenticatingRealm进行认证
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                username, password, getName());

        return simpleAuthenticationInfo;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //角色授权
        Set<String> roles = new HashSet<>();
        roles.add("role1");
        roles.add("role2");

        //资源授权
        Set<String> permission = new HashSet<>();
        permission.add("user_add");
        permission.add("user_select");

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permission);
        info.setRoles(roles);
        return info;
    }

}

Shiro加密

散列算法

散列算法一般用于生成数据的摘要信息,是一种不可逆的算法,一般适合存储密码之类的数据,常见的散列算法如 MD5、SHA 等。

散列算法测试

@Test
    public void testPwd(){
        //明文密码:
        String input="123456";

        //加密:md5
        Md5Hash md5Hash = new Md5Hash(input);
        System.out.println("md5Hash = " + md5Hash);
        //md5Hash = e10adc3949ba59abbe56e057f20f883e

        //加密: MD5+盐
        Md5Hash shiro = new Md5Hash(input, "shiro");
        System.out.println("shiro = " + shiro);
       // shiro = eef3a22a128d5adb5699e3c7da7a6fc8

        //加密: MD5 + 盐 + 散列次数
        Md5Hash shiro1 = new Md5Hash(input, "shiro", 2);
        System.out.println("shiro1 = " + shiro1);
       // shiro1 = b87dbd0bd4bcc6536a08d2027e329547

    }

初始化配置文件

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

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

#自定义 realm
ShiroRealm=cn.ybzy.shiro.security.ShiroRealm

#将realm设置到securityManager
securityManager.realms=$ShiroRealm1

自定义加密Realm

public class ShiroRealm extends AuthorizingRealm {

    @Override
    public String getName() {
        return "ShiroRealm";
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        //从token中 获取用户身份信息
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());

        //假设模拟从数据库中查询出的密码是加密后的密文 : 明文(123456) + 盐(shiro) + 散列次数(2)
        String pwd ="b87dbd0bd4bcc6536a08d2027e329547";
        //返回认证信息由父类AuthenticatingRealm进行认证
        SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
                username, pwd, ByteSource.Util.bytes("shiro"),getName());

        return simpleAuthenticationInfo;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

}

自定义加密Realm测试

 @Test
    public void testLoginAndLogout() {

        //获取SecurityManager工厂,使用Ini配置文件初始化SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro-password.ini");
        //通过工厂创建SecurityManager实例
       SecurityManager securityManager = factory.getInstance();
        //securityManager绑定给SecurityUtils
        SecurityUtils.setSecurityManager(securityManager);
        // 创建Subject实例,该实例认证要使用上边创建的securityManager进行
        Subject subject = SecurityUtils.getSubject();
        // 创建token令牌,记录用户认证的身份和凭证
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "123456");
        try {
            //登录,即身份验证
            subject.login(token);
        } catch (AuthenticationException e) {
            // 身份验证失败
            e.printStackTrace();
        }
        // 用户认证状态
        Boolean isAuthenticated = subject.isAuthenticated();
        System.out.println("用户认证状态:" + isAuthenticated);
        // 基于角色的授权:
        // 是否有某一个角色
        System.out.println("用户是否拥有一个角色:" + subject.hasRole("role1"));
        // 是否有多个角色
        System.out.println("用户是否拥有多个角色:" + subject.hasAllRoles(Arrays.asList("role1", "role2")));
        // 基于资源授权:
        System.out.println("是否拥有某一个权限:" + subject.isPermitted("user_add"));
        System.out.println("是否拥有多个权限:" + subject.isPermittedAll("user_create","user_delete"));
        // 用户退出
        subject.logout();
        isAuthenticated = subject.isAuthenticated();
        System.out.println("用户认证状态:" + isAuthenticated);
    }

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/137149.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!