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

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

  • Shiro简介
      • 1.什么是Shiro?
      • 2.Shiro的功能模块
      • 3.Shiro的外部结构
      • 4.Shiro的内部结构
  • 添加依赖环境
      • 方式一
      • 方式二
  • Shiro认证
      • 一、初始化配置文件
      • 二、Shiro认证测试
  • Shiro授权
      • 一、初始化配置文件
      • 二、Shiro授权测试
  • 自定义Realm
      • 一、初始化配置文件
      • 二、自定义Realm
      • 三、自定义Realm测试
  • 认证与授权的执行流程
      • (1) 认证流程
      • (2) 授权流程
  • Shiro加密
      • 散列算法测试
      • 初始化配置文件
      • 自定义加密Realm
      • 自定义加密Realm测试

Shiro简介

1.什么是Shiro?

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

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

2.Shiro的功能模块

Apache Shiro快速入门教程之初识Shiro(一)_第1张图片

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

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

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

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

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

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

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

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

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

"Remember Me":记住我。

3.Shiro的外部结构

Apache Shiro快速入门教程之初识Shiro(一)_第2张图片
Shiro就是一个最简单的应用:
1、应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;
2、我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。

4.Shiro的内部结构

Apache Shiro快速入门教程之初识Shiro(一)_第3张图片

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.shirogroupId>
            <artifactId>shiro-coreartifactId>
            <version>1.2.3version>
        dependency>
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-webartifactId>
            <version>1.2.3version>
        dependency>
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-springartifactId>
            <version>1.2.3version>
        dependency>
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-ehcacheartifactId>
            <version>1.2.3version>
        dependency>
        <dependency>
            <groupId>org.apache.shirogroupId>
            <artifactId>shiro-quartzartifactId>
            <version>1.2.3version>
        dependency>
        <dependency>
	      <groupId>junitgroupId>
	      <artifactId>junitartifactId>
	      <version>4.12version>
	      <scope>testscope>
    dependency>

方式二

	<dependency>
          <groupId>org.apache.shirogroupId>
          <artifactId>shiro-allartifactId>
          <version>1.2.3version>
     dependency>
      <dependency>
	      <groupId>junitgroupId>
	      <artifactId>junitartifactId>
	      <version>4.12version>
	      <scope>testscope>
    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域
Apache Shiro快速入门教程之初识Shiro(一)_第4张图片

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) 认证流程

Apache Shiro快速入门教程之初识Shiro(一)_第5张图片

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) 授权流程

Apache Shiro快速入门教程之初识Shiro(一)_第6张图片

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);
    }

你可能感兴趣的:(Apache,Shiro)