Shiro 实战教程(上)

1

注:该shiro教程来源于B站上的一个教程,因为源码是付费的,我就不分享了,下篇讲解springboot搭配shiro进行使用。

个人我的博客:java

天涯志

个人公众号:菜鸟小谢web

8

1.权限的管理

1.1 什么是权限管理

基本上涉及到用户参与的系统都要进行权限管理,权限管理属于系统安全的范畴,权限管理实现对用户访问系统的控制,按照安全规则或者安全策略控制用户能够访问并且只能访问本身被受权的资源。spring

权限管理包括用户身份认证受权两部分,简称认证受权。对于须要访问控制的资源用户首先通过身份认证,认证经过后用户具备该资源的访问权限方可访问。数据库

1.2 什么是身份认证

身份认证,就是判断一个用户是否为合法用户的处理过程。最经常使用的简单身份认证方式是系统经过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。对于采用指纹等系统,则出示指纹;对于硬件Key等刷卡系统,则须要刷卡。apache

1.3 什么是受权

受权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后须要分配权限方可访问系统的资源,对于某些资源没有权限是没法访问的编程


2.什么是shiro

Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.

Shiro 是一个功能强大且易于使用的Java安全框架,它执行身份验证、受权、加密和会话管理。使用Shiro易于理解的API,您能够快速轻松地保护任何应用程序—从最小的移动应用程序到最大的web和企业应用程序。浏览器

Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限受权、加密、会话管理等功能,组成了一个通用的安全认证框架。缓存


3.shiro的核心架构

2

3.1 Subject

Subject即主体,外部应用与subject进行交互,subject记录了当前操做用户,将用户的概念理解为当前操做的主体,多是一个经过浏览器请求的用户,也多是一个运行的程序。 Subject在shiro中是一个接口,接口中定义了不少认证授相关的方法,外部程序经过subject进行认证授,而subject是经过SecurityManager安全管理器进行认证受权安全

3.2 SecurityManager

SecurityManager即安全管理器,对所有的subject进行安全管理,它是shiro的核心,负责对全部的subject进行安全管理。经过SecurityManager能够完成subject的认证、受权等,实质上SecurityManager是经过Authenticator进行认证,经过Authorizer进行受权,经过SessionManager进行会话管理等。springboot

SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager这三个接口。

3.3 Authenticator

Authenticator即认证器,对用户身份进行认证,Authenticator是一个接口,shiro提供ModularRealmAuthenticator实现类,经过ModularRealmAuthenticator基本上能够知足大多数需求,也能够自定义认证器。

3.4 Authorizer

Authorizer即受权器,用户经过认证器认证经过,在访问功能时须要经过受权器判断用户是否有此功能的操做权限。

3.5 Realm

Realm即领域,至关于datasource数据源,securityManager进行安全认证须要经过Realm获取用户权限数据,好比:若是用户身份数据在数据库那么realm就须要从数据库获取用户身份信息。

  • ​ 注意:不要把realm理解成只是从数据源取数据,在realm中还有认证受权校验的相关的代码。

3.6 SessionManager

sessionManager即会话管理,shiro框架定义了一套会话管理,它不依赖web容器的session,因此shiro可使用在非web应用上,也能够将分布式应用的会话集中在一点管理,此特性可以使它实现单点登陆。

3.7 SessionDAO

SessionDAO即会话dao,是对session会话操做的一套接口,好比要将session存储到数据库,能够经过jdbc将会话存储到数据库。

3.8 CacheManager

CacheManager即缓存管理,将用户权限数据存储在缓存,这样能够提升性能。

3.9 Cryptography

Cryptography即密码管理,shiro提供了一套加密/解密的组件,方便开发。好比提供经常使用的散列、加/解密等功能。


4. shiro中的认证

4.1 认证

身份认证,就是判断一个用户是否为合法用户的处理过程。最经常使用的简单身份认证方式是系统经过核对用户输入的用户名和口令,看其是否与系统中存储的该用户的用户名和口令一致,来判断用户身份是否正确。

4.2 shiro中认证的关键对象

  • Subject:主体

访问系统的用户,主体能够是用户、程序等,进行认证的都称为主体;

  • Principal:身份信息

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

  • credential:凭证信息

是只有主体本身知道的安全信息,如密码、证书等。

4.3 认证流程

3

4.4 认证的开发

1. 建立项目并引入依赖
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.5.3</version>
</dependency>
2. 引入shiro配置文件并加入以下配置
[users]
xiaoxie=123
zhangsan=456

4

3.开发认证代码
public class TestAuthenticator {
    public static void main(String[] args) {
        //建立securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        defaultSecurityManager.setRealm(new IniRealm("classpath:shiro.ini"));
        //将安装工具类中设置默认安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //获取主体对象
        Subject subject = SecurityUtils.getSubject();
        //建立token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen1", "123");
        try {
            subject.login(token);//用户登陆
            System.out.println("登陆成功~~");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误!!");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误!!!");
        }

    }
}
  • DisabledAccountException(账号被禁用)
  • LockedAccountException(账号被锁定)
  • ExcessiveAttemptsException(登陆失败次数过多)
  • ExpiredCredentialsException(凭证过时)等

4.5 自定义Realm

上边的程序使用的是Shiro自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分状况下须要从系统的数据库中读取用户信息,因此须要自定义realm。

1.shiro提供的Realm

5

2.根据认证源码认证使用的是SimpleAccountRealm

6
SimpleAccountRealm的部分源码中有两个方法一个是 认证 一个是 受权,

public class SimpleAccountRealm extends AuthorizingRealm {
        //.......省略
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        SimpleAccount account = getUser(upToken.getUsername());

        if (account != null) {

            if (account.isLocked()) {
                throw new LockedAccountException("Account [" + account + "] is locked.");
            }
            if (account.isCredentialsExpired()) {
                String msg = "The credentials for account [" + account + "] are expired";
                throw new ExpiredCredentialsException(msg);
            }

        }

        return account;
    }

    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = getUsername(principals);
        USERS_LOCK.readLock().lock();
        try {
            return this.users.get(username);
        } finally {
            USERS_LOCK.readLock().unlock();
        }
    }
}
3.自定义realm
/**
 * 自定义realm
 */
public class CustomerRealm extends AuthorizingRealm {
    //认证方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    //受权方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String) token.getPrincipal();
        if("xiaochen".equals(principal)){
            return new SimpleAuthenticationInfo(principal,"123",this.getName());
        }
        return null;
    }
}
4.使用自定义Realm认证
public class TestAuthenticatorCusttomerRealm {
    public static void main(String[] args) {
        //建立securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //IniRealm realm = new IniRealm("classpath:shiro.ini");
        //设置为自定义realm获取认证数据
        defaultSecurityManager.setRealm(new CustomerRealm());
        //将安装工具类中设置默认安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //获取主体对象
        Subject subject = SecurityUtils.getSubject();
        //建立token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");
        try {
            subject.login(token);//用户登陆
            System.out.println("登陆成功~~");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误!!");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误!!!");
        }

    }
}

4.6 使用MD5和Salt

实际应用是将盐和散列后的值存在数据库中,自动realm从数据库取出盐和加密后的值由shiro完成密码校验。
1.自定义md5+salt的realm
/**
 * 自定义md5+salt realm
 */
public class CustomerRealm extends AuthorizingRealm {
    //认证方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    //受权方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String) token.getPrincipal();
        if("xiaochen".equals(principal)){
            String password = "3c88b338102c1a343bcb88cd3878758e";
            String salt = "Q4F%";
            return new SimpleAuthenticationInfo(principal,password, 
                                                ByteSource.Util.bytes(salt),this.getName());
        }
        return null;
    }
2.使用md5 + salt 认证
public class TestAuthenticatorCusttomerRealm {
    public static void main(String[] args) {
        //建立securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //IniRealm realm = new IniRealm("classpath:shiro.ini");
        //设置为自定义realm获取认证数据
        CustomerRealm customerRealm = new CustomerRealm();
        //设置md5加密
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(1024);//设置散列次数
        customerRealm.setCredentialsMatcher(credentialsMatcher);
        defaultSecurityManager.setRealm(customerRealm);
        //将安装工具类中设置默认安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //获取主体对象
        Subject subject = SecurityUtils.getSubject();
        //建立token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");
        try {
            subject.login(token);//用户登陆
            System.out.println("登陆成功~~");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误!!");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误!!!");
        }

    }
}

5. shiro中的受权

5.1 受权

受权,即访问控制,控制谁能访问哪些资源。主体进行身份认证后须要分配权限方可访问系统的资源,对于某些资源没有权限是没法访问的。

5.2 关键对象

受权可简单理解为who对what(which)进行How操做:

Who,即主体(Subject),主体须要访问系统中的资源。

What,即资源(Resource),如系统菜单、页面、按钮、类方法、系统商品信息等。资源包括资源类型资源实例,好比商品信息为资源类型,类型为t01的商品为资源实例,编号为001的商品信息也属于资源实例。

How,权限/许可(Permission),规定了主体对资源的操做许可,权限离开资源没有意义,如用户查询权限、用户添加权限、某个类方法的调用权限、编号为001用户的修改权限等,经过权限可知主体对哪些资源都有哪些操做许可。

5.3 受权流程

7

5.4 受权方式

  • 基于角色的访问控制

    • RBAC基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制

      if(subject.hasRole("admin")){
         //操做什么资源
      }
  • 基于资源的访问控制

    • RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制

      if(subject.isPermission("user:update:01")){ //资源实例
        //对01用户进行修改
      }
      if(subject.isPermission("user:update:*")){  //资源类型
        //对01用户进行修改
      }

5.5 权限字符串

​ 权限字符串的规则是:资源标识符:操做:资源实例标识符,意思是对哪一个资源的哪一个实例具备什么操做,“:”是资源/操做/实例的分割符,权限字符串也可使用*通配符。

例子:

  • 用户建立权限:user:create,或user:create:*
  • 用户修改实例001的权限:user:update:001
  • 用户实例001的全部权限:user:*:001

5.6 shiro中受权编程实现方式

  • 编程式

    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(“admin”)) {
        //有权限
    } else {
        //无权限
    }
  • 注解式

    @RequiresRoles("admin")
    public void hello() {
        //有权限
    }
  • 标签式

    JSP/GSP 标签:在JSP/GSP 页面经过相应的标签完成:
    <shiro:hasRole name="admin">
        <!— 有权限—>
    </shiro:hasRole>
    注意: Thymeleaf 中使用shiro须要额外集成!

5.7 开发受权

1.realm的实现
public class CustomerRealm extends AuthorizingRealm {
    //认证方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String primaryPrincipal = (String) principals.getPrimaryPrincipal();
        System.out.println("primaryPrincipal = " + primaryPrincipal);

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();

        simpleAuthorizationInfo.addRole("admin");

        simpleAuthorizationInfo.addStringPermission("user:update:*");
        simpleAuthorizationInfo.addStringPermission("product:*:*");


        return simpleAuthorizationInfo;
    }

    //受权方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String principal = (String) token.getPrincipal();
        if("xiaochen".equals(principal)){
            String password = "3c88b338102c1a343bcb88cd3878758e";
            String salt = "Q4F%";
            return new SimpleAuthenticationInfo(principal,password, 
                                                ByteSource.Util.bytes(salt),this.getName());
        }
        return null;
    }

}
2.受权
public class TestAuthenticatorCusttomerRealm {
    public static void main(String[] args) {
        //建立securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //IniRealm realm = new IniRealm("classpath:shiro.ini");
        //设置为自定义realm获取认证数据
        CustomerRealm customerRealm = new CustomerRealm();
        //设置md5加密
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("MD5");
        credentialsMatcher.setHashIterations(1024);//设置散列次数
        customerRealm.setCredentialsMatcher(credentialsMatcher);
        defaultSecurityManager.setRealm(customerRealm);
        //将安装工具类中设置默认安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //获取主体对象
        Subject subject = SecurityUtils.getSubject();
        //建立token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaochen", "123");
        try {
            subject.login(token);//用户登陆
            System.out.println("登陆成功~~");
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误!!");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误!!!");
        }
        //认证经过
        if(subject.isAuthenticated()){
            //基于角色权限管理
            boolean admin = subject.hasRole("admin");
            System.out.println(admin);

            boolean permitted = subject.isPermitted("product:create:001");
            System.out.println(permitted);
        }

    }
}

下一篇讲解shiro搭配SpringBoot进行开发,谢谢!!!

相关文章
相关标签/搜索