Shiro 实战教程(全)

目录

1.权限的管理

1.1 什么是权限管理

1.2 什么是身份认证

1.3 什么是授权

2.什么是shiro

3.shiro的核心架构

3.1 Subject

3.2 SecurityManager

3.3 Authenticator

3.4 Authorizer

3.5 Realm

3.6 SessionManager

3.7 SessionDAO

3.8 CacheManager

3.9 Cryptography

4. shiro中的认证

4.1 认证

4.2 shiro中认证的关键对象

4.3 认证流程

4.4 认证的开发

4.5 自定义Realm

4.6 使用MD5和Salt

5. shiro中的授权

5.1 授权

5.2 关键对象

5.3 授权流程

5.4 授权方式

5.5 权限字符串

5.6 shiro中授权编程实现方式

5.7 开发授权

6.整合SpringBoot项目实战

6.0 整合思路

6.1 创建springboot项目

6.2 引入shiro依赖

6.3 配置shiro环境

6.4 常见过滤器

6.5 认证实现

6.6 退出认证

6.7 MD5、Salt的认证实现

今天的分享就到此结束了

创作不易点赞评论互关三连


1.权限的管理

1.1 什么是权限管理

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

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

1.2 什么是身份认证

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

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的核心架构

3.1 Subject

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

3.2 SecurityManager

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

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

 

4.4 认证的开发

1. 创建项目并引入依赖

 

2. 引入shiro配置文件并加入如下配置

 

 

3.开发认证代码

/**
 * @author: mosin
 * @version: v1.0
 */
public class ShiroTest {

    public static void main(String[] args) {
        //创建默认的安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //创建安全管理器需要的realm对象
        IniRealm iniRealm = new IniRealm("classpath:realm.ini");
        //安全管理器设置realm对象
        defaultSecurityManager.setRealm(iniRealm);
        //将安全管理器注入安全工具类   用于获取认证的主体
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //获取认证的主体
        Subject subject = SecurityUtils.getSubject();
        //创建令牌
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosin", "1234");

        try {
            //认证 通过没有任何的异常
            subject.login(usernamePasswordToken);
            //验证是否通过
            boolean authenticated = subject.isAuthenticated();
            System.out.println("认证通过:"+authenticated);
        } 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

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

 

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("mosin".equals(principal)){
            return new SimpleAuthenticationInfo(principal,"123",this.getName());
        }
        return null;
    }
}

4.使用自定义Realm认证

public class TestAuthenticatorCustomerRealm {
    public static void main(String[] args) {
        //创建securityManager
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //IniRealm realm = new IniRealm("classpath:realm.ini");
        //设置为自定义realm获取认证数据
        defaultSecurityManager.setRealm(new CustomerRealm());
        //将安装工具类中设置默认安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //获取主体对象
        Subject subject = SecurityUtils.getSubject();
        //创建token令牌
        UsernamePasswordToken token = new UsernamePasswordToken("mosin", "1234");
        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 CustomerMD5Realm extends AuthorizingRealm {

    //授权
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
    //认证
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        String principal = (String) token.getPrincipal();
        //根据用户名查询数据库
        if("mosin".equals(principal)){
            // 参数1:用户名  参数2:密码 参数3:盐 参数4:自定义realm的名字
            System.out.println(this.getName());
            return new SimpleAuthenticationInfo(principal, "800d63a19662b2ba95bc2ffa01ab4804", ByteSource.Util.bytes("mosin"),this.getName());
        }
        return null;
    }
}

2.使用md5 + salt 认证

public class CustomerMD5RealmTest {

    public static void main(String[] args) {

        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //创建自定义MD5Realm对象
        CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm();
        //创建密码认证匹配器对象
        HashedCredentialsMatcher md5 = new HashedCredentialsMatcher("MD5");
        //设置散列的次数
        md5.setHashIterations(1024);
        //设置密码认证匹配器对象
        customerMD5Realm.setCredentialsMatcher(md5);
        //设置安全管理器的 认证安全数据源
        defaultSecurityManager.setRealm(customerMD5Realm);
        //设置安全工具类的安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);
        //获取认证的主体
        Subject subject = SecurityUtils.getSubject();
        //创建令牌
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosi", "12345");

        //登录认证
        try {
            subject.login(usernamePasswordToken);
            System.out.println("认证通过:"+subject.isAuthenticated());
        } 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 授权流程

5.4 授权方式

  • 基于角色的访问控制

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

        
  • 基于资源的访问控制

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

 

5.5 权限字符串

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

例子:

  • 用户创建权限:user:create,或user:create:*

  • 用户修改实例001的权限:user:update:001

  • 用户实例001的所有权限:user:*:001

5.6 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 salt = "Q4F%";
            return new SimpleAuthenticationInfo(principal,password, 
                                                ByteSource.Util.bytes(salt),this.getName());
        }
        return null;
    }

}

 

2.授权

public class CustomerMD5RealmTest {

    public static void main(String[] args) {

        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
        //创建自定义MD5Realm对象
        CustomerMD5Realm customerMD5Realm = new CustomerMD5Realm();
        //创建密码认证匹配器对象
        HashedCredentialsMatcher md5 = new HashedCredentialsMatcher("md5");
        //设置加密的次数
        md5.setHashIterations(1024);
        //设置密码认证匹配器对象
        customerMD5Realm.setCredentialsMatcher(md5);
        //设置安全管理器的 认证安全数据源
        defaultSecurityManager.setRealm(customerMD5Realm);
        //设置安全工具类的安全管理器
        SecurityUtils.setSecurityManager(defaultSecurityManager);

        //获取认证的主体
        Subject subject = SecurityUtils.getSubject();
        //创建令牌
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("mosin", "12345");

        //登录认证
        try {
            subject.login(usernamePasswordToken);
            System.out.println("认证通过:"+subject.isAuthenticated());
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            System.out.println("用户名错误");
        }catch (IncorrectCredentialsException e){
            e.printStackTrace();
            System.out.println("密码错误!!!");
        }

        //基于角色的控制
        //单角色控制
        System.out.println("========hasRole==========");
        boolean admin = subject.hasRole("admin");
        System.out.println("hash admin role:"+admin);

        //多角色控制
        System.out.println("========hasAllRoles==========");
        List<String> roles = Arrays.asList("admin", "user");
        boolean booleans = subject.hasAllRoles(roles);
        System.out.println("booleans = " + booleans);

        // 基于任意角色的控制
        System.out.println("========hasRoles==========");
        boolean[] booleans1 = subject.hasRoles(roles);
        for (boolean b : booleans1) {
            System.out.println("b = " + b);
        }

        //基于权限字符串的权限控制
        System.out.println("========isPermitted==========");
        boolean permitted = subject.isPermitted("user:update:*");
        System.out.println("permitted = " + permitted);

        //分别具有哪些权限
        boolean[] permitted1 = subject.isPermitted("user:update:*", "product:update:*");
        for (boolean b : permitted1) {
            System.out.println("b = " + b);
        }
        //同时具有哪些权限
        boolean permittedAll = subject.isPermittedAll("user:update:*", "product:update:*");
        System.out.println("permittedAll = " + permittedAll);

    }
}

6.整合SpringBoot项目实战

6.0 整合思路

6.1 创建springboot项目

6.2 引入shiro依赖

6.3 配置shiro环境

0.创建配置类

 

1.配置shiroFilterFactoryBean

 

2.配置WebSecurityManager

 

创建自定义realm

 

 

4.配置自定义realm

 

5.编写主页面index.jsp

 

6.启动项目,访问index.jsp

 

默认在配置好shiro环境后默认环境中没有对项目中任何资源进行权限控制,所有现在项目中所有资源都可以通过路径访问

7.加入权限控制

修改ShiroFilterFactoryBean配置

 

8.重启项目访问查看

 

6.4 常见过滤器

  • 注意: shiro提供和多个默认的过滤器,我们可以用这些过滤器来配置控制指定url的权限:

配置缩写 对应的过滤器 功能
anon AnonymousFilter 指定url可以匿名访问
authc FormAuthenticationFilter 指定url需要form表单登录,默认会从请求中获取usernamepassword,rememberMe等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑,但是一般都是我们自己在控制器写登录逻辑的,自己写的话出错返回的信息都可以定制嘛。
authcBasic BasicHttpAuthenticationFilter 指定url需要basic登录
logout LogoutFilter 登出过滤器,配置指定url就可以实现退出功能,非常方便
noSessionCreation NoSessionCreationFilter 禁止创建会话
perms PermissionsAuthorizationFilter 需要指定权限才能访问
port PortFilter 需要指定端口才能访问
rest HttpMethodPermissionFilter 将http请求方法转化成相应的动词来构造一个权限字符串,这个感觉意义不大,有兴趣自己看源码的注释
roles RolesAuthorizationFilter 需要指定角色才能访问
ssl SslFilter 需要https请求才能访问
user UserFilter 需要已登录或“记住我”的用户才能访问

6.5 认证实现

1. 在login.jsp中开发认证界面

 

2. 开发controller

@Controller
@RequestMapping("user")
public class UserController {
  /**
    * 用来处理身份认证
    * @param username
    * @param password
    * @return
    */
  @RequestMapping("login")
  public String login(String username,String password){
    //获取主体对象
    Subject subject = SecurityUtils.getSubject();
    try {
      subject.login(new UsernamePasswordToken(username,password));
      return  "redirect:/index.jsp";
    } catch (UnknownAccountException e) {
      e.printStackTrace();
      System.out.println("用户名错误!");
    }catch (IncorrectCredentialsException e){
      e.printStackTrace();
      System.out.println("密码错误!");
    }
    return "redirect:/login.jsp";
  }
}
  • 在认证过程中使用subject.login进行认证

3.开发realm中返回静态数据(未连接数据库)

4.启动项目以realm中定义静态数据进行认证

 

 

6.6 退出认证

1.开发页面退出连接

 

2.开发controller

 

3.修改退出连接访问退出路径

 

4.退出之后访问受限资源立即返回认证界面

 

6.7 MD5、Salt的认证实现

1.开发数据库注册

0.开发注册界面

1.创建数据表结构

 

 

2.项目引入依赖

 

3.配置application.properties配置文件

 

4.创建entity

 

5.创建DAO接口

 

6.开发mapper配置文件

 

7.开发service接口

 

8.创建salt工具类

 

9.开发service实现类

 

10.开发Controller

 

11.启动项目进行注册

 

2.开发数据库认证

0.开发DAO

 

1.开发mapper配置文件

 

2.开发Service接口

 

3.开发Service实现类

 

4.开发在工厂中获取bean对象的工具类

 

5.修改自定义realm

 

6.修改ShiroConfig中realm使用凭证匹配器以及hash散列

 

今天的分享就到此结束了

创作不易点赞评论互关三连

 

猜你喜欢

转载自blog.csdn.net/swy2560666141/article/details/130365895