SpringBoot——Shiro框架技术整合(课时二十三)

时间:2022-10-06 08:03:04

本案例实战框架用了以下技术

  • SpringBoot框架

  • Shiro的框架

  • MyBatis框架

  • Spring框架

  • thymeleaf技术

  • SSM三层框架

  • 数据库

实操部分

  • Subject——org.apache.shiro.subject.Subject

    特定于当前与软件交互的实体的安全视图

  • SecurityManager——org.apache.shiro.mgt.SecurityManager

    Shiro架构的核心,管理并协调各个组件共同完成安全工作,它还管理每个应用程序用户的Shiro视图,因此它知道如何为每个用户执行安全操作。

  • Authenticator——org.apache.shiro.authc.Authenticator

    Authenticator是负责认证的组件,当用户尝试登录时,登录是由Authenticator来执行的,Authenticator将会从Realms取出用户信息,来和用户提供的登录信息进行比对

    • Authenticator Strategy——org.apache.shiro.authc.pam.AuthenticationStrategy

      如果超过一个Realm被配置了,这个AuthenticationStrategy将会协调这些Realms来决定认证操作是否成功,比如,认证成功是需要所有的Realms都认证成功,还是仅仅一个Realm成功

  • Authorizer——org.apache.shiro.authz.Authorizer

    Authorizer是负责访问控制的组件,换句话说,它负责检测用户是否有执行某个操作的权限。和Authenticator一样,Authorizer也从多个Realms数据源中获取用户的角色信息和权限信息,通过这些信息来判断用户是否可以执行某个操作

  • SessionManager——org.apache.shiro.session.mgt.SessionManager

    注意不是SecurityManager,SessionManager负责创建和管理用户的Session周期。在安全框架中,Shiro提供了一个独有的特性,Shiro可以在任何环境中管理用户会话,即使实在非Web/Servlet和非EJB容器环境中,默认情况下,Shiro将会使用已有的会话机制,如Servlet容器,如果没有,Shiro将会使用内建的企业会话管理机制来管理会话。通过SessionDao,我们可以使用任何数据源来持久化Session

    • SessionDAO——org.apache.shiro.session.mgt.eis.SessionDAO

      SessionDAO代表SessionManager执行会话持久性(CRUD)操作。它允许将任何数据存储插入到Session Management基础结构中

  • CacheManager——org.apache.shiro.cache.CacheManager

    CacheManager负责创建Shiro中的其他组件的Cache实例并管理其生命周期,因为Shiro需要访问各种数据源来进行认证,授权和会话管理,缓存一直是框架中的一流架构特性,可以在使用这些数据源时提高性能,任何现代的开源或者企业缓存产品都可以接入Shiro中

  • Cryptography——org.apache.shiro.crypto.*

    Shiro的crypto包包含了易于使用和理解的常见加密算法实现。使用过Java自带的加密库的人都应该知道它很难使用,Shiro提供的加密API简化了复杂的Java机制,让加密更便于使用

  • Realms——org.apache.shiro.realm.Realm

    正如前面提到的,Realms是Shiro和应用程序数据的桥梁,当需要执行认证和授权时,Shiro会从至少一个Realm中寻找用户信息,应用程序中可以配置许多个Realm,Shiro会在必要时协调这些Realm

pom.xml文件信息配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.lop</groupId>
    <artifactId>shiro-springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>shiro-springboot</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!--        mybatis整合SpringBoot-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <!--整合包-->
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>


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

        <!--    log4j    -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.14</version>
        </dependency>

        <!--        MySql的依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>1.2.17</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.9.1</version>
        </dependency>


        <!--        Thymeleaf模板-->
        <!--Thymeleaf  说明基于3.0.1 模板引擎-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
            <version>3.0.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.xmlunit</groupId>
            <artifactId>xmlunit-core</artifactId>
            <version>2.5.1</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Yml文件信息

spring:
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource
    initial-size: 5
    min-idle: 5
    max-active: 20
    # 配置获取连接等待超时的时间
    max-wait: 60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    time-between-eviction-runs-millis: 60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    min-evictable-idle-time-millis: 300000
    validation-query: SELECT 1 FROM DUAL
    test-while-idle: true
    test-on-borrow: false
    test-on-return: false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    pool-prepared-statements: true
    #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 最好的功能
    max-pool-prepared-statement-per-connection-size: 20
    filters: stat,wall
    use-global-data-source-stat: true
    # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
    connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

SpringBoot——Shiro框架技术整合(课时二十三)

 index.html页面

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http:www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>

<div shiro:hasPermisssion="user:add">
    假如hasPermisssion有这个限制
    <a th:href="@{/user/add}">add</a>
</div>


<div align="center">
    <h1>index</h1>
    <!--/*@thymesVar id="msg" type="java"*/-->
    <p th:text="${msg}"></p>
    <a th:href="@{/user/add}">add</a>
    <br>
    <a th:href="@{/user/update}">update</a>

</div>

</body>
</html>

login.html页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<!--从Session判断值-->
<div align="center"  th:if="session.loginUser==null">
    <h1>登录</h1>
    <hr>
    <!--/*@thymesVar id="msg" type="java"*/-->
    <h2 th:text="${msg}" style="color: red;"></h2>
    <form  th:action="@{/login}" method="post">
        <p>用户名:<input type="text" name="username" placeholder="请输入用户名"></p>
        <p>用户密码:<input type="password" name="password" placeholder="请输入用户密码"></p>
        <p><input type="submit"></p>
    </form>
</div>
</body>
</html>
SpringBoot——Shiro框架技术整合(课时二十三)
add.html

 

SpringBoot——Shiro框架技术整合(课时二十三)
update.html

 数据层:

<?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">
<!--比要时候要加上-->
<!--suppress ALL -->
<mapper namespace="com.lop.mapper.UserMapper">
    <select id="queryUserByName" parameterType="String" resultType="User">
        select * from  mybatis.user where  name =#{name}
    </select>
</mapper>
package com.lop.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;


@Repository
@Mapper
public interface UserMapper {

    public UserMapper queryUserByName(String username);

}

Service层:

package com.lop.Service;

import com.lop.pojo.User;

public interface UserService {
    public User queryUserByName(String username);


}
package com.lop.Service;

import com.lop.mapper.UserMapper;
import com.lop.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceLmp implements UserService {
    @Autowired
    UserMapper userMapper;

    @Override
    public User queryUserByName(String username) {
        userMapper.queryUserByName(username);
        return null;
    }
}

控制层:

package com.lop.Controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 首页控制器
 */
@Controller

public class MyController {

    @RequestMapping({"/", "/index"})
    public String toIndex(Model model) {
        model.addAttribute("msg", "hllow");
        return "index";
    }


    @RequestMapping("user/add")
    public String add() {
        return "user/add";
    }


    @RequestMapping("user/update")
    public String update() {
        return "user/update";
    }


    @RequestMapping("/tologin")
    public String toLogin() {
        return "login";
    }

    @RequestMapping("/login")
    public String login(String username, String password, Model model) {
        //获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        //执行登录方法
        try {
            subject.login(token);


            return "index";
        } catch (UnknownAccountException e) {
            model.addAttribute("msg", "用户的名字步存在");
            return "login";
        } catch (IncorrectCredentialsException e) {
            model.addAttribute("msg", "用户的密码有误");
            return "login";
        }
    }

    @RequestMapping("/noauth")
    public  String unauthorized(){
        return "没有授权无法访问信息";

    }

}

认证授权

package com.lop.config;

import com.lop.Service.UserService;
import com.lop.pojo.User;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;


/**
 * 自定义的UserRealm
 */


public class UserRealm extends AuthorizingRealm {
    //把用户数据库拿过来 连接真实数据库
    @Autowired
    UserService  userService;
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("----------------------授权");

        //  SimpleAuthorizationInfo
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //打开没有授权的功能
        info.addStringPermission("uder:add");

        //拿到当前对象
        Subject subject = SecurityUtils.getSubject();
        User  currentUser = (User) subject.getPrincipal();//拿到user对象
        //设置当前用户的权限
        info.addStringPermission(currentUser.getPerms());

        return info;
    }

  
  //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("==========================认证");
    //        String name = "root";
    //        String password = "123456";

        UsernamePasswordToken usertoken = (UsernamePasswordToken) token;
        //连接真实数据库
       User user=userService.queryUserByName(usertoken.getUsername());

         if (user == null) {
             return null;
         }
        Subject cursubject = SecurityUtils.getSubject();
        Session session = cursubject.getSession();
        session.setAttribute("loginUser",user);


        //        if(!usertoken.getUsername().equals(name)){
    //            return null;
    //        }
        //密码认证
        return new SimpleAuthenticationInfo(user,user.getPassword(),"");
    }

ShiroConfig 代码解读:

package com.lop.config;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    //ShiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();

设置管理器

bean.setSecurityManager(defaultWebSecurityManager);

SpringBoot——Shiro框架技术整合(课时二十三)

 

  //授权的信息内容 有user:add 没有授权
        filterMap.put("/user/add", "perms[user:add]");
        filterMap.put("/user/update", "perms[user:update]");
 //用户没有授权请前往这个页面
        bean.setLoginUrl("/noauth");
 //设置登录请求
        bean.setLoginUrl("/tologin");

        bean.setFilterChainDefinitionMap(filterMap);

        return bean;
    }
 // 自定义的UserRealm
    //    @Bean(name = "userRealm")
    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }

SpringBoot——Shiro框架技术整合(课时二十三)

 

<html lang="en" xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http:www.thymeleaf.org/thymeleaf-extras-shiro">
<div shiro:hasPermisssion="user:add">
    假如hasPermisssion有这个限制
    <a th:href="@{/user/add}">add</a>
</div>
<div align="center">
    <h1>index</h1>
    <!--/*@thymesVar id="msg" type="java"*/-->
    <p th:text="${msg}"></p>
    <a th:href="@{/user/add}">add</a>
    <br>
    <a th:href="@{/user/update}">update</a>

</div>
SpringBoot——Shiro框架技术整合(课时二十三)
login的登录

 

SpringBoot——Shiro框架技术整合(课时二十三)
SpringBoot

 

理论介绍:

//设置管理器
        bean.setSecurityManager(defaultWebSecurityManager);
        //增加shiro的内置过滤器
        /**
         * anon
         * authc
         * perms
         * role
         */
        Map<String, String> filterMap = new LinkedHashMap<>();
//        filterMap.put("/user/add","authc");
//        filterMap.put("/user/update","authc");
        filterMap.put("/user/*", "authc");

        //授权的信息内容 有user:add 没有授权
        filterMap.put("/user/add", "perms[user:add]");
        filterMap.put("/user/update", "perms[user:update]");

        //用户没有授权请前往这个页面
        bean.setLoginUrl("/noauth");

        //设置登录请求
        bean.setLoginUrl("/tologin");

        bean.setFilterChainDefinitionMap(filterMap);

        return bean;
    }

    //DefaultWebSecurityManager
    //DefaultWebSecurityManager 管理
    @Bean("securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {

        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    // 自定义的UserRealm
    //    @Bean(name = "userRealm")
    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }

    /*整合包 thymeleaf-extras-shiro */
    @Bean
    public  SiroDialect getSiroDialect() {
        return new SiroDialect();
    }