Shiro

Shiro

1.权限的管理

什么是权限管理?

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

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

2.认证(Authentication)

2.1 认证的概念

用户访问系统的控制

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

2.2 认证中抽象出的关键对象

  • Subject:主体

  • Principal:身份信息 username

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

  • credential:凭证信息 password

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

  • token :令牌 token=身份信息+凭证信息

3.授权(Authorization)

3.1授权概念

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

3.2 授权中抽象出的关键对象

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

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

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

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

权限分为粗颗粒和细颗粒,粗颗粒权限是指对资源类型的权限,细颗粒权限是对资源实例的权限。

一般配置的都是粗颗粒

4.权限模型

在这里插入图片描述

用户表

用户id 用户名 用户密码

1 xiaohei 111111

2 xiaohuang 2222222

角色表

id 角色名

11 admin

12 superAdmin

13 ssAdmin

用户角色表

id 用户id 角色id

a 1 11

b 1 12

c 2 13

权限表

id 权限标识

22 1: 轮播图管理

23 2:专辑管理

24 3:文章管理

25 4: 用户管理

26 5:管理员管理

27 7:查询工资

资源表

id 资源标识

32 轮播图管理

33 专辑管理

34 文章管理

35 用户管理

36 管理员管理

角色权限表

id 角色id 权限id

80 11 22

81 11 23

在这里插入图片描述

5.权限控制方案

用户拥有了权限即可操作权限范围内的资源,系统不知道主体是否具有访问权限需要对用户的访问进行控制。

5.1 基于角色的访问控制

​ RBAC基于角色的访问控制(Role-Based Access Control)是以角色为中心进行访问控制,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等,访问控制流程如下:

在这里插入图片描述

上图中的判断逻辑代码可以理解为:

if(主体.hasRole(“总经理角色id”)){

​ 查询工资

}

缺点:以角色进行访问控制粒度较粗,如果上图中查询工资所需要的角色变化为总经理和部门经理,此时就需要修改判断逻辑为“判断主体的角色是否是总经理或部门经理”,系统可扩展性差。

修改代码如下:

if(主体.hasRole(“总经理角色id”) || 主体.hasRole(“部门经理角色id”)){

​ 查询工资

}

5.1 基于资源的访问控制

RBAC基于资源的访问控制(Resource-Based Access Control)是以资源为中心进行访问控制,比如:主体必须具有查询工资权限才可以查询员工工资信息等,访问控制流程如下:

上图中的判断逻辑代码可以理解为:

if(主体.hasPermission(“查询工资权限标识”)){

​ 查询工资

}

优点:系统设计时定义好查询工资的权限标识,即使查询工资所需要的角色变化为总经理和部门经理也只需要将“查询工资信息权限”添加到“部门经理角色”的权限列表中,判断逻辑不用修改,系统可扩展性强。

基于角色和资源的访问控制

6.Shrio

6.1 什么是shiro

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

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.

Apache Shiro™是一个功能强大且易于使用的Java安全框架,它执行身份验证,授权,加密和会话管理。 使用Shiro易于理解的API,您可以快速轻松地保护任何应用程序-从最小的移动应用程序到最大的Web和企业应用程序。

6.2 为什么要学shiro

​ 既然shiro将安全认证相关的功能抽取出来组成一个框架,使用shiro就可以非常快速的完成认证、授权等功能的开发,降低系统成本。

​ shiro使用广泛,shiro可以运行在web应用,非web应用,集群分布式应用中越来越多的用户开始使用shiro。

​ java领域中spring security(原名Acegi)也是一个开源的权限管理框架,但是spring security依赖spring运行,而shiro就相对独立,最主要是因为shiro使用简单、灵活,所以现在越来越多的用户选择shiro。

6.3 Shiro核心架构图

在这里插入图片描述

7.第一个Shiro认证程序

7.1.创建项目
7.2.引入相关的依赖
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.3.2</version>
</dependency>
7.3.shiro第一个认证程序
@Test
public void contextLoads() {
    Subject subject = null;

    try {
        //初始化安全管理器工厂
        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");

        //通过安全管理器工厂创建一个安全管理器
        SecurityManager securityManager = factory.createInstance();

        //将安全管理器放入安全工具类   自动使用
        SecurityUtils.setSecurityManager(securityManager);

        //通过安全工具类获取主体对象
        subject = SecurityUtils.getSubject();

        //创建一个认证的令牌   token=身份信息+凭证信息
        AuthenticationToken token = new UsernamePasswordToken("dfsf","111111");

        //认证
        subject.login(token);
    } catch (UnknownAccountException e) {
        System.out.println("未知的账号异常");
    }catch (IncorrectCredentialsException e) {
        System.out.println("不正确的凭证异常");
    }

    //判断是否认证成功
    boolean authenticated = subject.isAuthenticated();
    System.out.println(authenticated);
}

注意:shiro是通过抛异常的形式告诉我们是否认证通过,一般会抛出两个异常

  • UnknownAccountException 未知的账号异常 用户不存在
  • IncorrectCredentialsException 不正确的凭证异常 密码错误
7.4.配置shiro的配置文件
[users]

xiaohei=111111
xiaohuang=222222

8.源码追踪中的关键类

//抽象类
AuthenticatingRealm  
 
//凭证匹配器  接口
private CredentialsMatcher credentialsMatcher;  
//默认提供   SimpleCredentialsMatcher


//抽象方法
protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken var1) throws AuthenticationException;
//  是AuthenticatingRealm 的实现类 
SimpleAccountRealm extends AuthenticatingRealm

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

    UsernamePasswordToken upToken = (UsernamePasswordToken)token;
    
    //根据用户名查询用户信息     upToken.getUsername()="xiaohei"
    SimpleAccount account = this.getUser(upToken.getUsername());
    //account   xiaohei,111111
    
    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;
}
CredentialsMatcher
//凭证匹配器
SimpleCredentialsMatcher

//做比对   equals  比对结果  true  false
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        Object tokenCredentials = this.getCredentials(token);  xiaohei   111111
        Object accountCredentials = this.getCredentials(info); xiaohei   111111
        return this.equals(tokenCredentials, accountCredentials);
}

9.shiro认证连接数据库

9.1.写自定义Realm

写自定义Realm 继承AuthenticatingRealm

public class MyRealm extends AuthenticatingRealm {

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //从令牌中获取身份信息
        String username = (String) token.getPrincipal();

        //查询数据库
        //select * from user where username=#{username}
        // User("1","xiaohei","111111")
        AuthenticationInfo info=null;

        if(username.equals("xiaohei")){
            //将数据库查询到的数据封装到info中
            info = new SimpleAuthenticationInfo("xiaohei","111111",this.getName());
        }
        return info;
    }
}

9.2.配置连接的配置

[main]
#自定义 realm
MyRealm=com.baizhi.conf.MyRealm

#将realm设置到securityManager

securityManager.realms=$MyRealm

10.shiro加密认证

​ shiro默认使用的凭证匹配器是:SimpleCredentialsMatcher,

此匹配器比对凭证信息的策略是equals,所以我们需要自定义凭证匹配器

10.1.加密算法

Shiro支持的加密算法: MD2,MD5,SHA1,SHA256,SHA384,SHA512,HASH散列

@Test
public void contextLoads() {

    //Md5Hash md5Hash = new Md5Hash("111111");
    //Md5Hash md5Hash = new Md5Hash("111111","ABCD");
    //Md5Hash md5Hash = new Md5Hash("111111","ABCD",1024);
    //96e79218965eb72c92a549dd5a330112   111111
    //2cdc750a3f0d0aadfac93bf6bf369699   ABCD + 111111
    //e0504e77b06f5a26faf37d044c65992b   ABCD + 111111 + hash散列 1024

    //1 xiaohei  e0504e77b06f5a26faf37d044c65992b  ABCD

    //Sha256Hash sha256Hash = new Sha256Hash("111111");
    //bcb15f821479b4d5772bd0ca866c00ad5f926e3580720659cc80d39c9d09802a

    //Sha384Hash sha384Hash = new Sha384Hash("111111");
    //1b0268a40ae44c012946c974d60bf5291e7bb7c63cdb72a904d9283e3dc0a34de9afebe4035665768aaa503a4e7a30c3

    //Sha512Hash sha512Hash = new Sha512Hash("111111");
    //b0412597dcea813655574dc54a5b74967cf85317f0332a2591be7953a016f8de56200eb37d5ba593b1e4aa27cea5ca27100f94dccd5b04bae5cadd4454dba67d

    Md5Hash md5Hash = new Md5Hash("111111","ABCD",1024);
    String s = md5Hash.toHex();
    System.out.println(s);
}
10.2.配置加密策略
方案一:
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
#告诉凭证匹配器使用哪个加密算法
credentialsMatcher.hashAlgorithmName=MD5
#hash散列迭代次数
credentialsMatcher.hashIterations=1024

#将自定义的凭证匹配器告诉 自定义Realm
MyRealm.credentialsMatcher=$credentialsMatcher

方案二:
#定义凭证匹配器
credentialsMatcher=org.apache.shiro.authc.credential.Md5CredentialsMatcher
#hash散列迭代次数
credentialsMatcher.hashIterations=1024

#将自定义的凭证匹配器告诉 自定义Realm
MyRealm.credentialsMatcher=$credentialsMatcher

11.shiro授权

extends AuthorizingRealm类实现里面的认证(doGetAuthenticationInfo)和**授权(doGetAuthorizationInfo)**的方法

11.1授权
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

    //1.查询用户有哪些权限
    String username = (String)principalCollection.getPrimaryPrincipal();

    //  根据用户主身份去查询该用户有哪些角色
    // admin sadmin supers
    //  根据用户主身份去查询角色对应有哪些权限

    //2.授予相关权限
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    if(username.equals("xiaohei")){
        //授予角色
        //info.addRole("admin");
        //授予角色集合
        info.addRoles(Arrays.asList("admin","sadmin","supers"));

        //授予权限
        //info.addStringPermission("user:query");
        //授予权限集合
        info.addStringPermissions(Arrays.asList("user:query","user:update"));

    }
    return info;
}
11.2 判断权限方式
a.基于角色的访问控制
//判断该主体是否有该角色
boolean hasRole = subject.hasRole("admin");

//判断该主体是否含有这些角色
boolean[] hasRoles = subject.hasRoles(Arrays.asList("admin", "supers","user"));

//判断该主体是否含有所有角色
boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("admin", "supers"));
b.基于权限的访问控制
//判断该主体是否含有该权限
boolean isPermitted = subject.isPermitted("user:delete");

//判断该主体是否含有这些权限
boolean[] isPermitteds = subject.isPermitted("user:query", "user:update");

//判断该主体是否含有所有权限
boolean idPermittedAll = subject.isPermittedAll("user:query", "user:update");
11.3 权限字符串规则

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

user:query

user:update

例子:

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

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

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

12 案例

12.1 认证授权

package com.baizhi.conf;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import java.util.Arrays;

public class MyRealm extends AuthorizingRealm {

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        //1.查询用户有哪些权限
        String username = (String)principalCollection.getPrimaryPrincipal();

        //  根据用户主身份去查询该用户有哪些角色
        // admin sadmin supers
        //  根据用户主身份去查询角色对应有哪些权限

        //2.授予相关权限
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        if(username.equals("xiaohei")){
            //授予角色
            //info.addRole("admin");
            //授予角色集合
            info.addRoles(Arrays.asList("admin","sadmin","supers"));

            //授予权限
            //info.addStringPermission("user:query");
            //授予权限集合
            info.addStringPermissions(Arrays.asList("user:query","user:update"));

        }
        return info;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //从令牌中获取身份信息
        String username = (String) token.getPrincipal();

        //查询数据库
        //select * from user where username=#{username}
        // User("1","xiaohei","111111")
        //1 xiaohei  e0504e77b06f5a26faf37d044c65992b  ABCD

        AuthenticationInfo info=null;

        if(username.equals("xiaohei")){

            //将数据库查询到的数据封装到info中  密文数据
            info = new SimpleAuthenticationInfo("xiaohei","e0504e77b06f5a26faf37d044c65992b", ByteSource.Util.bytes("ABCD"),this.getName());
        }
        return info;
    }
}

12.2 测试认证授权:

package com.baizhi;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Arrays;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ShiroApplicationTests {

    @Test
    public void contextLoads() {
        Subject subject = null;

        try {
            //初始化安全管理器工厂
            IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");

            //通过安全管理器工厂创建一个安全管理器
            SecurityManager securityManager = factory.createInstance();

            //将安全管理器放入安全工具类   自动使用
            SecurityUtils.setSecurityManager(securityManager);

            //通过安全工具类获取主体对象
            subject = SecurityUtils.getSubject();

            //创建一个认证的令牌   token=身份信息+凭证信息
            AuthenticationToken token = new UsernamePasswordToken("xiaohei","111111");

            //认证
            subject.login(token);

        } catch (UnknownAccountException e) {
            //e.printStackTrace();
            System.out.println("未知的账号异常");
            //UnknownAccountException    未知的账号异常   用户不存在
            //IncorrectCredentialsException  不正确的凭证异常    密码错误
        }catch (IncorrectCredentialsException e) {
            //e.printStackTrace();
            System.out.println("不正确的凭证异常");
            //UnknownAccountException    未知的账号异常   用户不存在
            //IncorrectCredentialsException  不正确的凭证异常    密码错误
        }

        //判断是否认证成功
        boolean authenticated = subject.isAuthenticated();

        //判断认证是否成功
        if(authenticated){
            /**基于角色的访问控制 授权*/

            //判断该主体是否有该角色
            //boolean b = subject.hasRole("admin");

            //判断该主体是否含有这些角色
            //boolean[] b = subject.hasRoles(Arrays.asList("admin", "supers","user"));

            //判断该主体是否含有所有角色
            boolean allRoles = subject.hasAllRoles(Arrays.asList("admin", "supers"));

            /**基于权限的访问控制 授权*/

            //判断该主体是否含有该权限
            //boolean b = subject.isPermitted("user:delete");

            //判断该主体是否含有这些权限
            //boolean[] b = subject.isPermitted("user:query", "user:update");

            //判断该主体是否含有所有权限
            boolean permittedAll = subject.isPermittedAll("user:query", "user:update");

            System.out.println("角色授权:"+allRoles);
            System.out.println("权限授权:"+permittedAll);

        }
    }
}

13.Shiro集成SpringBoot项目

在这里插入图片描述

13.1.创建sprinBoot项目

父项目版本2.1.6.RELEASE

13.2.引入相关依赖
<!--shiro的jar包-->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.2</version>
</dependency>

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
    <version>1.4.2</version>
</dependency>

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.4.2</version>
</dependency>

<!--shiro与spring整合包 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

<!--web支持的jar springboot的启动器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--测试支持的jar-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <!-- 只在test测试里面运行 -->
    <scope>test</scope>
</dependency>

<!-- 给内嵌tomcat提供jsp解析功能的jar-->
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>

<dependency>
    <groupId>jstl</groupId>
    <artifactId>jstl</artifactId>
    <version>1.2</version>
</dependency>
13.3.配置springboot相关配置
server:
  port: 8989
  servlet:
    context-path: /shiro
    jsp:
      init-parameters:
        development: true
spring:
  mvc:
    view:
      prefix: /
      suffix: .jsp
13.4.配置shiroFilter配置
@Configuration
public class ShiroFilterConf {

    //将shiro过滤器工厂交给spring工厂管理
    @Bean
    public ShiroFilterFactoryBean getUser(SecurityManager securityManager){
        //创建shiro过滤器工厂
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
        //将安全管理器交给shiro过滤器工厂
        factoryBean.setSecurityManager(securityManager);

        HashMap<String, String> map = new HashMap<>();

        /*
        *FormAuthenticationFilter authc 认证过滤器  只有认证成功的资源才能被访问
        *AnonymousFilter anon  匿名过滤器  不用认证都可以访问
        * */
        map.put("/**","authc");
        map.put("/test/login.jsp","anon");
        map.put("/user/login","anon");

        //定拦截策略  定义一个过滤器链
        factoryBean.setFilterChainDefinitionMap(map);

        //自定义拦截跳转的登录页面
        factoryBean.setLoginUrl("/test/login.jsp");


        return factoryBean;
    }

    //将安全管理器spring工厂管理
    @Bean
    public SecurityManager getSecurityManager(){
        //创建安全管理器
        SecurityManager securityManager = new DefaultWebSecurityManager();

        return securityManager;
    }
}
13.5.Shiro过滤器
过滤器简称对应的java类
anonorg.apache.shiro.web.filter.authc.AnonymousFilter
authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
portorg.apache.shiro.web.filter.authz.PortFilter
restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter
sslorg.apache.shiro.web.filter.authz.SslFilter
userorg.apache.shiro.web.filter.authc.UserFilter
logoutorg.apache.shiro.web.filter.authc.LogoutFilter

anon:例子/admins/**=anon 没有参数,表示可以匿名使用。

authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数

13.6.认证

自定义Realm类 继承AuthorizingRealm类 实现doGetAuthenticationInfo方法

13.6.1.认证方法

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

    System.out.println("认证");

    //获取身份信息
    String username = (String)authenticationToken.getPrincipal();

    AuthenticationInfo info=null;
    //去数据库查询
    if(username.equals("xiaohei")){

        //将数据库查询到的数据封装到info中  密文数据
        info = new SimpleAuthenticationInfo("xiaohei","e0504e77b06f5a26faf37d044c65992b", ByteSource.Util.bytes("ABCD"),this.getName());

    }
    return info;
}

13.6.2.认证配置

//将安全管理器对象交给spring工厂管理
@Bean
public SecurityManager getSecurityManager(MyRealm myRealm){
    //创建安全管理器
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

    //将自定义Realm交给安全管理器
    securityManager.setRealm(myRealm);
    return securityManager;
}

//将自定义Realm对象交给spring工厂管理
@Bean
public MyRealm getMyRealm(HashedCredentialsMatcher credentialsMatcher){
    //配置自定义Realm
    MyRealm myRealm = new MyRealm();

    //将凭证匹配器给自定义Realm
    myRealm.setCredentialsMatcher(credentialsMatcher);
    return myRealm;
}

//将凭证匹配器对象交给spring工厂管理
@Bean
public HashedCredentialsMatcher getHashedCredentialsMatcher(){

    //创建凭证匹配器
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    credentialsMatcher.setHashAlgorithmName("MD5"); //设置加密算法
    credentialsMatcher.setHashIterations(1024);  //设置散列次数
    return credentialsMatcher;
}

13.6.3 登陆登出

@RequestMapping("login")
public String login(String username,String password){
    try {
        //认证  获得主题对象
        Subject subject = SecurityUtils.getSubject();

        //封装token=username+password
        AuthenticationToken token=new UsernamePasswordToken(username,password);
        //认证
        subject.login(token);

        return "redirect:/main/main.jsp";
    } catch (UnknownAccountException e) {
        System.out.println("未知的账号异常==账号不存在");
        return "redirect:/test/login.jsp";
    }catch (IncorrectCredentialsException e) {
        System.out.println("不正确的凭证异常==密码错误");
        return "redirect:/test/login.jsp";
    }
}

@RequestMapping("logout")
public String logout(){
    //认证  获得主体对象
    Subject subject = SecurityUtils.getSubject();
    //退出
    subject.logout();
    return "redirect:/test/login.jsp";
}
13.7.shiro中的标签

13.7.1.在页面引入

<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

13.7.2.shiro标签

//没有认证展示 未登录  执行标签体的内容
<shiro:notAuthenticated></shiro:notAuthenticated>
//认证成功之后展示   已登录  执行标签体的内容
<shiro:authenticated></shiro:authenticated>
//获取身份信息
<shiro:principal/>
//判断该主体是否含有该角色
<shiro:hasRole name="user"></shiro:hasRole>
//判断该主体是否含有这些角色
<shiro:hasAnyRoles name="admin"></shiro:hasAnyRoles>
//判断该主体是否含有该权限
<shiro:hasPermission name="user:update"></shiro:hasPermission>
13.8.授权

自定义Realm类 集成 AuthorizingRealm类 实现doGetAuthorizationInfo方法

//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

    System.out.println("授权");

    //获取主身份信息
    String username =(String)principalCollection.getPrimaryPrincipal();
    //授权
    //根据用户身份查角色
    //根据角色查询权限

    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

    if(username.equals("xiaohei")){
        //添加角色
        info.addRoles(Arrays.asList("user","admin"));

        //添加权限
        info.addStringPermissions(Arrays.asList("user:query","user:update","admin:query"));
    }
    return info;
    }
13.9.shiro配置缓存

13.9.1.导入缓存jar包

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>1.4.2</version>
</dependency>

13.9.2.配置缓存交给安全管理器

//将缓存对象交给spring工厂管理
@Bean
public CacheManager getCacheManager(){

    //创建缓存
    CacheManager cacheManager = new EhCacheManager();
    return cacheManager;
}

//将安全管理器对象交给spring工厂管理
@Bean
public SecurityManager getSecurityManager(MyRealm myRealm,CacheManager cacheManager){
    //创建安全管理器
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

    //将自定义Realm交给安全管理器
    securityManager.setRealm(myRealm);

    //配置缓存
    securityManager.setCacheManager(cacheManager);
    return securityManager;
}

14.Shiro连接数据库

14.1准备数据库表
/*
Navicat MySQL Data Transfer

Source Server         : MySQL
Source Server Version : 50528
Source Host           : localhost:3306
Source Database       : shiro

Target Server Type    : MYSQL
Target Server Version : 50528
File Encoding         : 65001

Date: 2019-08-13 16:39:03
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for s_admin
-- ----------------------------
DROP TABLE IF EXISTS `s_admin`;
CREATE TABLE `s_admin` (
  `admin_id` varchar(50) NOT NULL DEFAULT '',
  `username` varchar(50) DEFAULT NULL,
  `password` varchar(50) DEFAULT NULL,
  `salt` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`admin_id`),
  UNIQUE KEY `s_admin_admin_id_uindex` (`admin_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of s_admin
-- ----------------------------
INSERT INTO `s_admin` VALUES ('1', 'nanan', 'a2c9ec06b8c0a2be811dfd47be6e5f82', 'asdfaf');
INSERT INTO `s_admin` VALUES ('2', 'bobo', 'c3f2b09474f65a0bb8eda78e3682955f', 'abcd');

-- ----------------------------
-- Table structure for s_admin_role
-- ----------------------------
DROP TABLE IF EXISTS `s_admin_role`;
CREATE TABLE `s_admin_role` (
  `id` varchar(50) DEFAULT NULL,
  `admin_id` varchar(50) DEFAULT NULL,
  `role_id` varchar(50) DEFAULT NULL,
  UNIQUE KEY `s_admin_role_id_uindex` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of s_admin_role
-- ----------------------------
INSERT INTO `s_admin_role` VALUES ('1', '1', '2');
INSERT INTO `s_admin_role` VALUES ('2', '1', '1');
INSERT INTO `s_admin_role` VALUES ('3', '1', '4');
INSERT INTO `s_admin_role` VALUES ('4', '1', '3');

-- ----------------------------
-- Table structure for s_authority
-- ----------------------------
DROP TABLE IF EXISTS `s_authority`;
CREATE TABLE `s_authority` (
  `authority_id` varchar(50) DEFAULT NULL,
  `authority_name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of s_authority
-- ----------------------------
INSERT INTO `s_authority` VALUES ('1', 'admin:query');
INSERT INTO `s_authority` VALUES ('2', 'admin:delete');
INSERT INTO `s_authority` VALUES ('3', 'admin:update');
INSERT INTO `s_authority` VALUES ('4', 'admin:insert');

-- ----------------------------
-- Table structure for s_role
-- ----------------------------
DROP TABLE IF EXISTS `s_role`;
CREATE TABLE `s_role` (
  `role_id` varchar(50) DEFAULT NULL,
  `role_name` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of s_role
-- ----------------------------
INSERT INTO `s_role` VALUES ('1', 'common');
INSERT INTO `s_role` VALUES ('2', 'super');
INSERT INTO `s_role` VALUES ('3', 'admin');
INSERT INTO `s_role` VALUES ('4', 'user');

-- ----------------------------
-- Table structure for s_role_authority
-- ----------------------------
DROP TABLE IF EXISTS `s_role_authority`;
CREATE TABLE `s_role_authority` (
  `id` varchar(50) DEFAULT NULL,
  `role_id` varchar(50) DEFAULT NULL,
  `authority_id` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of s_role_authority
-- ----------------------------
INSERT INTO `s_role_authority` VALUES ('1', '2', '1');
INSERT INTO `s_role_authority` VALUES ('2', '2', '2');
INSERT INTO `s_role_authority` VALUES ('3', '2', '3');
INSERT INTO `s_role_authority` VALUES ('4', '2', '4');
14.2.导入连接数据库的相关依赖
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.4</version>
    <scope>provided</scope>
</dependency>

<!--整合mybatis-->
<!--mybatis和springboot的整合包-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>

<!--数据源-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.29</version>
</dependency>

<!--数据库驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>

<!--mybatis的核心jar-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>
14.3配置数据库连接参数
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shiro
    username: root
    password: root
mybatis:
  type-aliases-package: com.baizhi.entity
  mapper-locations: classpath:com/baizhi/mapper/*Mapper.xml
14.4 配置实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Admin {

    private String adminId;
    private String username;
    private String password;
    private String salt;

    //关系属性 一个用户对应多个角色
    private ArrayList<Role> roles;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {
    private String roleId;
    private String roleName;

    //关系属性 一个角色对应多个权限
    private ArrayList<Authority> authorities;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Authority {
    private String authorityId;
    private String authorityName;
}
14.5 配置DAO
public interface AdminDao {

    Admin queryByUsername(String username);

    Admin queryByUsernames(String username);

}
14.6.配置Mapper文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.baizhi.dao.AdminDao">

    <resultMap id="queryMap" type="Admin">
        <id column="aAdminId" property="adminId"/>
        <result column="username" property="username" />
        <result column="password" property="password" />
        <result column="salt" property="salt" />
        <collection property="roles" ofType="Role" >
            <id column="rRoleId" property="roleId"/>
            <result column="rRoleName" property="roleName" />
            <collection property="authorities" ofType="Authority">
                <id column="auAuthorityId" property="authorityId"/>
                <result column="auAuthorityName" property="authorityName" />
            </collection>
        </collection>
    </resultMap>

    <!--根据用户名查询用户s-->
    <select id="queryByUsernames" resultMap="queryMap">
        select
            a.admin_id aAdminId,a.username username,a.password password,a.salt salt,
            r.role_id rRoleId,r.role_name rRoleName,
            au.authority_id auAuthorityId,au.authority_name auAuthorityName
        from s_admin a
        left join s_admin_role ar on a.admin_id=ar.admin_id
        left join s_role r on ar.role_id=r.role_id
        left join s_role_authority ra on r.role_id=ra.role_id
        left join s_authority au on ra.authority_id=au.authority_id
        where username=#{username}
    </select>

    <sql id="querySql">
        admin_id adminId,username,password,salt
    </sql>

    <!--根据用户名查询用户-->
    <select id="queryByUsername" resultType="Admin">
        select <include refid="querySql"/> from s_admin where username=#{username}
    </select>

</mapper>
14.7.认证授权连接数据库
package com.baizhi.realm;

import com.baizhi.entity.Admin;
import com.baizhi.entity.Authority;
import com.baizhi.entity.Role;
import com.baizhi.service.AdminService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;

import javax.annotation.Resource;
import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;

import static java.awt.SystemColor.info;

public class MyRealm extends AuthorizingRealm {

    @Resource
    private AdminService adminService;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        System.out.println("授权");

        //获取主身份信息
        String username =(String)principalCollection.getPrimaryPrincipal();

        //授权
        //根据用户身份查角色
        //根据角色查询权限
        //根据用户名查角色和权限
        Admin admin = adminService.queryByUsernames(username);

        //角色集合
        ArrayList<String> roleList = new ArrayList<>();
        //权限集合
        ArrayList<String> PermissionList = new ArrayList<>();

        //获取所有的角色
        ArrayList<Role> roles = admin.getRoles();
        //遍历角色
        for (Role role : roles) {
            //获取角色名称
            String roleName = role.getRoleName();
            //将角色放入角色集合
            roleList.add(roleName);

            //获取权限集合
            ArrayList<Authority> authorities = role.getAuthorities();

            //遍历权限集合
            for (Authority authority : authorities) {

                //获取权限名称
                String authorityName = authority.getAuthorityName();

                //将权限放入权限集合
                PermissionList.add(authorityName);
            }
        }

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //添加角色
            info.addRoles(roleList);
            //添加权限
            info.addStringPermissions(PermissionList);
        return info;
    }

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

        System.out.println("认证");

        //获取身份信息
        String username = (String)authenticationToken.getPrincipal();

        //根据用户名查询用户
        Admin admin = adminService.queryByUsername(username);

        //将数据库查询到的数据封装到info中  密文数据
        AuthenticationInfo info= new SimpleAuthenticationInfo(admin.getUsername(),admin.getPassword(),ByteSource.Util.bytes(admin.getSalt()),this.getName());
        /*if(username.equals("xiaohei")){
            info = new SimpleAuthenticationInfo("xiaohei","e0504e77b06f5a26faf37d044c65992b", ByteSource.Util.bytes("ABCD"),this.getName());
        }*/
        return info;
    }
}

ities();

        //遍历权限集合
        for (Authority authority : authorities) {

            //获取权限名称
            String authorityName = authority.getAuthorityName();

            //将权限放入权限集合
            PermissionList.add(authorityName);
        }
    }

    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //添加角色
        info.addRoles(roleList);
        //添加权限
        info.addStringPermissions(PermissionList);
    return info;
}

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

    System.out.println("认证");

    //获取身份信息
    String username = (String)authenticationToken.getPrincipal();

    //根据用户名查询用户
    Admin admin = adminService.queryByUsername(username);

    //将数据库查询到的数据封装到info中  密文数据
    AuthenticationInfo info= new SimpleAuthenticationInfo(admin.getUsername(),admin.getPassword(),ByteSource.Util.bytes(admin.getSalt()),this.getName());
    /*if(username.equals("xiaohei")){
        info = new SimpleAuthenticationInfo("xiaohei","e0504e77b06f5a26faf37d044c65992b", ByteSource.Util.bytes("ABCD"),this.getName());
    }*/
    return info;
}

}






















































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

如玉少年-_-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值