shiro(详解)

这里写自定义目录标题


生活不如意,快去敲# shiro

Apache旗下的一款安全权限框架

shiro可以完成:认证,加密,授权,会话管理,以及web集成,

什么是shiro

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

Apache Shiro 特性

Apache Shiro是一个全面的、蕴含丰富功能的安全框架

什么是权限管理

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

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

什么是身份认证

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

什么是授权

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

Subject

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

SecurityManager

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

Authenticator

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

Authorizer

Authorizer即授权器,用户通过认证器认证通过,在访问功能时需要通过授权器判断用户是否有此功能的操作权限。

Realm

Realm即领域,相当于datasource数据源,securityManager进行安全认证需要通过Realm获取用户权限数据,比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。

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

SessionManager

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

SessionDAO

SessionDAO即会话dao,是对session会话操作的一套接口,比如要将session存储到数据库,可以通过jdbc将会话存储到数据库。

CacheManager

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

Cryptography

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

面试题

1,Shiro可以做哪些工作?

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

shiro的核心架构

认证的开发

1. 创建项目并引入依赖
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-core</artifactId>
  <version>1.5.3</version>
</dependency>
2. 引入shiro配置文件并加入如下配置
[users]
xiaochen=123
zhangsan=456
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("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("密码错误!!!");
        }

    }
}
  • 自定义Realm

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

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

    自定义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;
        }
    }
    
    使用自定义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("密码错误!!!");
            }
    
        }
    }
    

    使用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("密码错误!!!");
            }
    
        }
    }
    

    shiro中的授权

    授权

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

    关键对象

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

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

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

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

    权限字符串

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

    例子:

    • 用户创建权限:user:create,或user:create:*
    • 用户修改实例001的权限:user:update:001
    • 用户实例001的所有权限:user:*:001

    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需要额外集成!
      

    开发授权

    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需要额外集成!
      
    1.realm的实现
    public class CustomerMd5Realm1 extends AuthorizingRealm {
        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("========================");
            String primaryPrincipal = (String) principalCollection.getPrimaryPrincipal();
            System.out.println("身份信息:"+primaryPrincipal);
    
            //根据用户信息 用户名  获取当前用户角色信息,以及权限信息 xiaochen  admin user
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            //将数据库中查询的角色信息赋值给全新对象
            simpleAuthorizationInfo.addRole("admin");
            simpleAuthorizationInfo.addRole("user");
            //将数据库中擦寻权限信息赋值给权限对象
            simpleAuthorizationInfo.addStringPermission("user:*:01");
    
            return simpleAuthorizationInfo;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
           //获取身份信息
            String principal = (String) authenticationToken.getPrincipal();
    
            //根据用户名去数据库查找信息
           if ("xiaochen".equals(principal)){
               return new SimpleAuthenticationInfo(principal,
                       "a3834da2205138a63d1c254361a7054a",
                       ByteSource.Util.bytes("qwertyuiop"),
                       this.getName());
           }
    
            return null;
        }
    }
    
    2.授权
    public class TestCustomerMD51 {
        public static void main(String[] args) {
            //创建安全管理器
            DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
            //注入realm
            CustomerMd5Realm1 realm1 = new CustomerMd5Realm1();
            //设置realm使用hash凭证匹配器
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            //使用算法
            credentialsMatcher.setHashAlgorithmName("md5");
            //散列次数
            credentialsMatcher.setHashIterations(1024);
            realm1.setCredentialsMatcher(credentialsMatcher);
            defaultSecurityManager.setRealm(realm1);
    
            //全局工具类放到安全管理器中
            SecurityUtils.setSecurityManager(defaultSecurityManager);
            //主体 subject
            Subject subject = SecurityUtils.getSubject();
            //创建令牌
            UsernamePasswordToken token = new UsernamePasswordToken("xiaochen","123");
    
            try {
                subject.login(token);
                System.out.println("认证状态"+subject.isPermittedAll());
            } catch (UnknownAccountException e) {
                e.printStackTrace();
                System.out.println("账号错误");
            } catch (IncorrectCredentialsException e){
                e.printStackTrace();
                System.out.println("密码错误");
    
            }
            //认证用户进行授权
            if(subject.isPermittedAll()){
                //1,基于角色的权限控制
                System.out.println(subject.hasRole("admin"));
                //2,基于多个角色
                System.out.println(subject.hasAllRoles(Arrays.asList("admin", "user")));
                //3基于多约束是否觉有其中一个角色
                boolean[] booleans = subject.hasRoles(Arrays.asList("admin", "user", "super"));
                for (boolean aBoolean : booleans) {
                    System.out.println(aBoolean);
                }
                System.out.println("==========================");
                //基于权限字符串的访问控制  资源识别付  操作 资源类型
                System.out.println("权限"+subject.isPermitted("user:update:01"));
    
            }
    
        }
    }
    

shiro架构

核心

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IfMicglN-1624702127692)(C:\Users\曹雪峰\AppData\Roaming\Typora\typora-user-images\1620385643680.png)]

1.Shiro三个核心组件

Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

SecurityManager:安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ru7uMyor-1624702127693)(C:\Users\曹雪峰\AppData\Roaming\Typora\typora-user-images\1624281727814.png)]

面试题

Shiro工作流程

应用代码通过Subject来进行认证和授权,而Subject又委托给SecurityManager;

我们需要给Shiro的SecurityManager注入Realm,从而让SecurityManager能得到合法的用户及其权限进行判断。

shiro核心组件:

1、UsernamePasswordToken,Shiro 用来封装用户登录信息,使用用户的登录信息来创建令牌 Token,这里shiro会直接将密码和前面生成token中的密码进行匹配,如果匹配成功则登陆成功,不成功则报错

2、SecurityManager,Shiro 的核心部分,负责安全认证和授权。

3、Suject,Shiro 的一个抽象概念,包含了用户信息。

4、Realm,开发者自定义的模块,根据项目的需求,验证和授权的逻辑全部写在 Realm 中。

5、AuthenticationInfo,用户的角色信息集合,认证时使用。

6、AuthorzationInfo,角色的权限信息集合,授权时使用。

7、DefaultWebSecurityManager,安全管理器,开发者自定义的 Realm 需要注入到DefaultWebSecurityManager 进行管理才能生效。

8、ShiroFilterFactoryBean,过滤器工厂,Shiro 的基本运行机制是开发者定制规则,Shiro 去执行,具体的执行操作就是由 ShiroFilterFactoryBean 创建的一个个 Filter 对象来完成。

Shiro和Spring Security比较

Shiro比SpringSecurity更容易使用,实现和最重要的理解

Spring Security更加知名的唯一原因是因为品牌名称
“Spring”以简单而闻名,但讽刺的是很多人发现安装Spring Security很难

然而,Spring Security却有更好的社区支持
Apache Shiro在Spring Security处理密码学方面有一个额外的模块

Spring-security 对spring 结合较好,如果项目用的springmvc ,使用起来很方便。但是如果项目中没有用到spring,那就不要考虑它了。
Shiro 功能强大、且 简单、灵活。是Apache 下的项目比较可靠,且不跟任何的框架或者容器绑定,可以独立运行

2. Shiro 特点

(1)易于理解的 Java Security API;
(2)简单的身份认证(登录),支持多种数据源
(3)对角色的简单的签权(访问控制)
(4)支持一级缓存,以提升应用程序的性能;
(6)异构客户端会话访问;
(7)非常简单的加密 API;
(8)不跟任何的框架或者容器捆绑,可以独立运行

面试题

Apache Shiro 的三大核心组件:*

1、Subject :当前用户的操作

2、SecurityManager:用于管理所有的Subject

3、Realms:用于进行权限信息的验证

用户角色权限

用户、角色及授权三者关系

Shiro的授权模式采用Rbac模式,意思就是,谁,扮演了什么角色,被允许执行什么操作,其中的“谁”就是user,其中扮演什么角色(role),操作就是权限(permission);

RBAC 认为授权实际上是WhoWhatHow 三元组之间的关系,也就是WhoWhat 进行How 的操作,也就是“主体”对“客体”的操作。

Who:是权限的拥有者或主体(如:User,Role)。

What:是操作或对象(operation,object)。**

How:具体的权限(Privilege,正向授权与负向授权)

springboot整合shiro的思路

整合SpringBoot和shiro

1 引入shiro jsp mybatis mysql druid shiro和ehcache 的依赖

<!--引入jsp解析依赖-->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>

        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>


        <!--引入shiro整合springboot依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.5.3</version>
        </dependency>

        <!--引入mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.19</version>
        </dependency>

        <!--引入shiro和ehcache-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.5.3</version>
        </dependency>

2 配置shiro环境

1.配置shiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager){
  //创建shiro的filter
  ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
  //注入安全管理器
  shiroFilterFactoryBean.setSecurityManager(securityManager);
 	
  return shiroFilterFactoryBean;
}
2.配置WebSecurityManager
@Bean
public DefaultWebSecurityManager getSecurityManager(Realm realm){
  DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
  defaultWebSecurityManager.setRealm(realm);
  return defaultWebSecurityManager;
}
3.创建自定义realm
public class CustomerRealm extends AuthorizingRealm {
    //处理授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }
		//处理认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws 
      																																		AuthenticationException {
        return null;
    }
}
4.配置自定义realm
//创建自定义realm
@Bean
public Realm getRealm(){
  return new CustomerRealm();
}
5.编写控制器跳转至index.html
@Controller
public class IndexController {
    @RequestMapping("index")
    public String index(){
        System.out.println("跳转至主页");
        return "index";
    }
}
6.启动springboot应用访问index
  • 注意:
    • 默认在配置好shiro环境后默认环境中没有对项目中任何资源进行权限控制,所有现在项目中所有资源都可以通过路径访问
7.加入权限控制
  • 修改ShiroFilterFactoryBean配置

    //注入安全管理器
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    Map<String,String> map =  new LinkedHashMap<>();
    map.put("/**","authc");
    //配置认证和授权规则
    shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
    
    • /** 代表拦截项目中一切资源 authc 代表shiro中的一个filter的别名,详细内容看文档的shirofilter列表

5 常见过滤器

  • 注意: shiro提供和多个默认的过滤器,我们可以用这些过滤器来配置控制指定url的权限:
Filter解释
anon无参,开放权限,可以理解为匿名用户或游客
authc无参,需要认证
logout无参,注销,执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(); 也就是退出
authcBasic无参,表示 httpBasic 认证
user无参,表示必须存在用户,当登入操作时不做检查
ssl无参,表示安全的URL请求,协议为 https
perms[user]参数可写多个,表示需要某个或某些权限才能通过,多个参数时写 perms[“user, admin”],当有多个参数时必须每个参数都通过才算通过
roles[user]参数可写多个,表示是某个或某些角色才能通过,多个参数时写 roles[“admin,user”],当有多个参数时必须每个参数都通过才算通过
rest[user]根据请求的方法,相当于 perms[user:method],其中 method 为 post,get,delete 等
port[8081]当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString 其中 schmal 是协议 http 或 https 等等,serverName 是你访问的 Host,8081 是 Port 端口,queryString 是你访问的 URL 里的 ? 后面的参数

1. 在login.jsp中开发认证界面
<form action="${pageContext.request.contextPath}/user/login" method="post">
  用户名:<input type="text" name="username" > <br/>
  密码  : <input type="text" name="password"> <br>
  <input type="submit" value="登录">
</form>
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中返回静态数据(未连接数据库)
@Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("==========================");
        String principal = (String) token.getPrincipal();
        if("xiaochen".equals(principal)){
            return new SimpleAuthenticationInfo(principal,"123",this.getName());
        }
        return null;
    }
}
  • 认证功能没有md5和随机盐的认证就实现啦

7 退出认证

1.开发controller
@Controller
@RequestMapping("user")
public class UserController {
  /**
    * 退出登录
    *
    */
  @RequestMapping("logout")
  public String logout(){
    Subject subject = SecurityUtils.getSubject();
    subject.logout();//退出用户
    return "redirect:/login.jsp";
  }
}

8 MD5、Salt的认证实现

1.开发数据库注册
1.开发注册界面
<h1>用户注册</h1>
<form action="${pageContext.request.contextPath}/user/register" method="post">
  用户名:<input type="text" name="username" > <br/>
  密码  : <input type="text" name="password"> <br>
  <input type="submit" value="立即注册">
</form>
2配置application.properties配置文件
server.port=8888
server.servlet.context-path=/shiro
spring.application.name=shiro

spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp
#新增配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root


mybatis.type-aliases-package=com.baizhi.springboot_jsp_shiro.entity
mybatis.mapper-locations=classpath:com/baizhi/mapper/*.xml

2.创建entity
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String  id;
    private String username;
    private String password;
    private String salt;
}

3.创建DAO接口
@Mapper
public interface UserDAO {
    void save(User user);
}
4.开发mapper配置文件
<insert id="save" parameterType="com.xhizhi.entity.User" useGeneratedKeys="true" keyProperty="id">
        insert into user(id,username,password,selt) values(#{id},#{username},#{password},#{selt})
    </insert>
5.开发service接口
public interface UserService {
    //注册用户方法
    void register(User user);
}
6.创建salt工具类
public class SaltUtils {
    /**
     * 生成salt的静态方法
     * @param n
     * @return
     */
    public static String getSalt(int n){
        char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890!@#$%^&*()".toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < n; i++) {
            char aChar = chars[new Random().nextInt(chars.length)];
            sb.append(aChar);
        }
        return sb.toString();
    }
}
7.开发service实现类
@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDAO userDAO;

    @Override
    public void register(User user) {
        //处理业务调用dao
        //1.生成随机盐
        String salt = SaltUtils.getSalt(8);
        //2.将随机盐保存到数据
        user.setSalt(salt);
        //3.明文密码进行md5 + salt + hash散列
        Md5Hash md5Hash = new Md5Hash(user.getPassword(),salt,1024);
        user.setPassword(md5Hash.toHex());
        userDAO.save(user);
    }
}
8.开发Controller
@Controller
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 用户注册
     */
    @RequestMapping("register")
    public String register(User user) {
        try {
            userService.register(user);
            return "redirect:/login.jsp";
        }catch (Exception e){
            e.printStackTrace();
            return "redirect:/register.jsp";
        }
    }
}

2.开发数据库认证
0.开发DAO
@Mapper
public interface UserDAO {

    void save(User user);
		//根据身份信息认证的方法
    User findByUserName(String username);
}
1.开发mapper配置文件
<select id="findByUserName" parameterType="String" resultType="User">
        select id,username,password,selt from user
        where username = #{username}
    </select>
2.开发Service接口
public interface UserService {
    //注册用户方法
    void register(User user);
    //根据用户名查询业务的方法
    User findByUserName(String username);
}
3.开发Service实现类
@Service("userService")
@Transactional
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDAO userDAO;
    @Override
    public User findByUserName(String username) {
        return userDAO.findByUserName(username);
    }
}
4.开发在工厂中获取bean对象的工具类
@Component
public class ApplicationContextUtils implements ApplicationContextAware {

    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.context = applicationContext;
    }


    //根据bean名字获取工厂中指定bean 对象
    public static Object getBean(String beanName){
        return context.getBean(beanName);
    }
}
5.修改自定义realm
 @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("==========================");

        //根据身份信息
        String principal = (String) token.getPrincipal();
        //在工厂中获取service对象
        UserService userService = (UserService) ApplicationContextUtils.getBean("userService");
				//根据身份信息查询
        User user = userService.findByUserName(principal);

        if(!ObjectUtils.isEmpty(user)){
            //返回数据库信息
            return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), 
                                               ByteSource.Util.bytes(user.getSalt()),this.getName());
        }
        return null;
    }
6.修改ShiroConfig中realm使用凭证匹配器以及hash散列
@Bean
public Realm getRealm(){
  CustomerRealm customerRealm = new CustomerRealm();
  //设置hashed凭证匹配器
  HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
  //设置md5加密
  credentialsMatcher.setHashAlgorithmName("md5");
  //设置散列次数
  credentialsMatcher.setHashIterations(1024);
  customerRealm.setCredentialsMatcher(credentialsMatcher);
  return customerRealm;
}

6.8 授权实现

0.页面资源授权
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

<shiro:hasAnyRoles name="user,admin">
        <li><a href="">用户管理</a>
            <ul>
                <shiro:hasPermission name="user:add:*">
                <li><a href="">添加</a></li>
                </shiro:hasPermission>
                <shiro:hasPermission name="user:delete:*">
                    <li><a href="">删除</a></li>
                </shiro:hasPermission>
                <shiro:hasPermission name="user:update:*">
                    <li><a href="">修改</a></li>
                </shiro:hasPermission>
                <shiro:hasPermission name="user:find:*">
                    <li><a href="">查询</a></li>
                </shiro:hasPermission>
            </ul>
        </li>
        </shiro:hasAnyRoles>
        <shiro:hasRole name="admin">
            <li><a href="">商品管理</a></li>
            <li><a href="">订单管理</a></li>
            <li><a href="">物流管理</a></li>
        </shiro:hasRole>
1.代码方式授权
@RequestMapping("save")
public String save(){
  System.out.println("进入方法");
  //获取主体对象
  Subject subject = SecurityUtils.getSubject();
  //代码方式
  if (subject.hasRole("admin")) {
    System.out.println("保存订单!");
  }else{
    System.out.println("无权访问!");
  }
  //基于权限字符串
  //....
  return "redirect:/index.jsp";
}
2.方法调用授权
  • @RequiresRoles 用来基于角色进行授权
  • @RequiresPermissions 用来基于权限进行授权
@RequiresRoles(value={"admin","user"})//用来判断角色  同时具有 admin user
@RequiresPermissions("user:update:01") //用来判断权限字符串
@RequestMapping("save")
public String save(){
  System.out.println("进入方法");
  return "redirect:/index.jsp";
}
4.创建dao方法
 //根据用户名查询所有角色
User findRolesByUserName(String username);
//根据角色id查询权限集合
List<Perms> findPermsByRoleId(String id);
5.mapper实现
<resultMap id="userMap" type="User">
        <id column="uid" property="id"/>
        <result column="username" property="username"/>
        <!--角色信息-->
        <collection property="roles" javaType="list" ofType="Role">
            <id column="id" property="id"/>
            <result column="rname" property="name"/>
        </collection>
    </resultMap>

    <select id="findRolesName" parameterType="String" resultMap="userMap">
        SELECT u.id uid,u.username,r.id,r.name rname
        FROM user u
        LEFT JOIN user_role ur
        ON u.id = ur.userid
        LEFT JOIN role r
        ON ur.roleid = r.id
        WHERE u.username=#{username}
    </select>

    <select id="findPermsByRoleId" parameterType="String" resultType="Perms">
      SELECT p.id,p.name,p.url,r.name FROM role r
        LEFT JOIN role_perms rp
        ON r.id = rp.roleid
        LEFT JOIN perms p
        ON rp.prmsid = p.id
        WHERE r.id=#{id}
    </select>
6.Service接口
//根据用户名查询所有角色
User findRolesByUserName(String username);
//根据角色id查询权限集合
List<Perms> findPermsByRoleId(String id);
7.Service实现
@Override
public List<Perms> findPermsByRoleId(String id) {
  return userDAO.findPermsByRoleId(id);
}

@Override
public User findRolesByUserName(String username) {
  return userDAO.findRolesByUserName(username);
}
8.修改自定义realm
public class CustomerRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //获取身份信息
        String primaryPrincipal = (String) principals.getPrimaryPrincipal();
        System.out.println("调用授权验证: "+primaryPrincipal);
        //根据主身份信息获取角色 和 权限信息
        UserService userService = (UserService) ApplicationContextUtils.getBean("userService");
        User user = userService.findRolesByUserName(primaryPrincipal);
        //授权角色信息
        if(!CollectionUtils.isEmpty(user.getRoles())){
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            user.getRoles().forEach(role->{
                simpleAuthorizationInfo.addRole(role.getName());
                //权限信息
                List<Perms> perms = userService.findPermsByRoleId(role.getId());
                if(!CollectionUtils.isEmpty(perms)){
                    perms.forEach(perm->{
                        simpleAuthorizationInfo.addStringPermission(perm.getName());
                    });
                }
            });
            return simpleAuthorizationInfo;
        }
        return null;
    }
}
9.启动测试

6.9 使用CacheManager

1.Cache 作用
  • Cache 缓存: 计算机内存中一段数据
  • 作用: 用来减轻DB的访问压力,从而提高系统的查询效率
  • 流程:
2.使用shiro中默认EhCache实现缓存
1.开启缓存
//3.创建自定义realm
    @Bean
    public Realm getRealm(){
        CustomerRealm customerRealm = new CustomerRealm();
        //修改凭证校验匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //设置加密算法为md5
        credentialsMatcher.setHashAlgorithmName("MD5");
        //设置散列次数
        credentialsMatcher.setHashIterations(1024);
        customerRealm.setCredentialsMatcher(credentialsMatcher);

        //开启缓存管理器
        customerRealm.setCachingEnabled(true);
        customerRealm.setAuthorizationCachingEnabled(true);
        customerRealm.setAuthorizationCachingEnabled(true);
        customerRealm.setCacheManager(new EhCacheManager());
        return customerRealm;
    }

  • 8
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值