【基础框架】Shrio安全框架
创始人
2025-05-29 05:05:46

Shrio安全框架

一、权限管理

1.1 什么是权限管理?

不同身份的用户进入到系统所能够完成的操作是不相同的,我们对不同用户进行的可执行的操作的管理称之为权限管理。

在这里插入图片描述

1.2 如何实现权限管理?

权限管理设计

  • 基于主页的权限管理(不同用户使用不同的主页,权限通过主页功能菜单进行限制)

    • 适用于权限管理比较单一、用户少、每类用户权限固定
  • 基于用户和权限的权限管理

    • 可以实现权限的动态分配,但是不够灵活
  • 基于角色的访问控制

    • RBAC 基于角色的访问控制

二、安全框架简介

2.1 认证授权流程

  • 认证:对用户的身份进行检查(登录验证)

  • 授权:对用户的权限进行检查(是否有对应的操作权限)

  • 流程示意图:

在这里插入图片描述

2.2 安全框架

  • 帮助我们完成用户身份认证及权限检查功能框架
  • 常用的安全框架:
    • Shiro:Apache Shiro是一个功能强大并且易用的Java安全框架 (小而简单)
    • Spring Security:基于Spring的一个安全框架,依赖Spring
    • OAuth2:第三方授权登录
    • 自定义安全认证中心

2.3 Shiro

  • Apache Shiro是一个功能强大并且易用的Java安全框架
  • 可以完成用户认证、授权、密码及会话管理
  • 可以在任何的应用系统中使用(主要针对单体项目的权限管理)

三、Shiro的工作原理

3.1 Shiro的核心功能

在这里插入图片描述

  • Anthentication 认证,验证用户是否有相应的身份—登录认证;

  • Authorization 授权,即权限验证;对已经通过认证的用户检查是否具有某个权限或者角色,从而控制是否能够进行某种操作;

  • Session Managment 会话管理,用户在认证成功之后创建会话,在没有退出之前,当前用户的所有信息都会保存在这个会话中;可以是普通的JavaSE应用,也可以是web应用;

  • Cryptography 加密,对敏感信息进行加密处理,shiro就提供这种加密机制;

  • 支持的特性:

    • Web Support — Shiro提供了过滤器,可以通过过滤器拦截web请求来处理web应用的访问控制
    • Caching 缓存支持,shiro可以缓存用户信息以及用户的角色权限信息,可以提高执行效率
    • Concurrency, shiro支持多线程应用
    • Testing 提供测试支持
    • Run As 允许一个用户以另一种身份去访问
    • Remeber Me
  • 说明:Shiro是一个安全框架,不提供用户、权限的维护(用户的权限管理需要我们自己去设计)

3.2 Shiro核心组件

  • Shiro三大核心组件:SubjectSecurity ManagerRealms
    • Subject,表示待认证和授权的用户
    • Security Manager,它是Shiro框架的核心,Shiro就是通过Security Manager来进行内部实例的管理,并通过它来提供安全管理的各种服务。
      • Authenticator,认证器
      • Anthorizer,授权器
      • SessionManager,会话管理器
      • CacheManager,缓存管理器
    • Realm,相当于Shiro进行认证和授权的数据源,充当了Shiro与安全数据之间的“桥梁”或者“连接器”。也就是说,当对用户进行认证(登录)和授权(访问控制)验证时,Shiro会用应用配置的Realm中查找用户及其权限信息。
在这里插入图片描述

四、基于JavaSE应用—Shiro的基本使用

4.1 创建Maven项目

4.2 导入Shiro依赖库

org.apache.shiroshiro-core1.4.1

4.3 创建Shiro配置文件

  • 在resource目录下创建名为shiro.ini的文件
  • 在文件中完成用户、角色及权限的配置
[users]
zhangsan=123456,seller
lisi=666666,ckmgr
admin=222222,admin[roles]
admin=*
seller=order-add,order-del,order-list
ckmgr=ck-add,ck-del,ck-list

4.4 Shiro的基本使用

package com.qfedu.shiro;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;import java.util.Scanner;public class TestShiro {public static void main(String[] args) {Scanner scan = new Scanner(System.in);System.out.println("请输入帐号:");String username = scan.nextLine();System.out.println("请输入密码:");String password = scan.nextLine();//1.创建安全管理器DefaultSecurityManager securityManager = new DefaultSecurityManager();//2.创建realmIniRealm iniRealm = new IniRealm("classpath:shiro.ini");//3.将realm设置给安全管理器securityManager.setRealm(iniRealm);//4.将Realm设置给SecurityUtils工具SecurityUtils.setSecurityManager(securityManager);//5.通过SecurityUtils工具类获取subject对象Subject subject = SecurityUtils.getSubject();//【认证流程】//a.将认证帐号和密码封装到token对象中UsernamePasswordToken token = new UsernamePasswordToken(username,password);//b.通过subject对象调用login方法进行认证申请:boolean b = false;try{subject.login(token);b = true;}catch(IncorrectCredentialsException e){b = false;}System.out.println(b?"登录成功":"登录失败");//【授权】//判断是否有某个角色System.out.println(subject.hasRole("seller"));//判断是否有某个权限boolean permitted = subject.isPermitted("order-del");System.out.println(permitted);}}

五、整合Shrio

5.1 SpringMVC整合Shrio

  • 1.导入依赖

    org.apache.shiroshiro‐core1.4.1
    org.apache.shiroshiro‐web1.4.1
    org.apache.shiroshiro‐spring1.4.1
    
    
  • 2.在web.xml中添加shrio对应的过滤器配置

    • 拦截所有请求

      
      contextConfigLocationclasspath:spring‐*.xmlorg.springframework.web.context.ContextLoaderListenerspringMVCorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:spring‐context*.xmlspringMVC*.doshiroFilterorg.springframework.web.filter.DelegatingFilterProxytargetFilterLifecycletrueshiroFilter/*
      
      
  • 3.在spring配置文件中,配置shrio框架所需的内容

    • 创建spring-context-shiro.xml
    • 完成shiro配置
    
    ‐‐>/**=anon
    
    
  • 4.配置权限验证使用注解

    
    
    
    
    

5.2 SSM整合Shiro进行认证及授权

  • 1.创建用户权限表,并添加数据

    • 因为此案例目前使用的是shiro默认数据库结构,则需要按照shiro默认的数据表结构进行创建及配置

    • 用户信息

      create table users(
      id int primary key auto_increment,
      username varchar( 60 ) not null, 
      password varchar( 60 ) not null,password_salt varchar( 100 )
      );
      
    • 添加用户信息

    insert into users(username,password)
    values('zhangsan','e10adc3949ba59abbe56e057f20f883e');
    insert into users(username,password)
    values('lisi','e10adc3949ba59abbe56e057f20f883e');
    insert into users(username,password)
    values('wangwu','e10adc3949ba59abbe56e057f20f883e');
    insert into users(username,password)
    values('zhaoliu','e10adc3949ba59abbe56e057f20f883e');
    insert into users(username,password)
    values('chenqi','e10adc3949ba59abbe56e057f20f883e');
    
    • 角色信息
    create table user_roles(
    id int primary key auto_increment,
    username varchar( 60 ) not null,
    role_name varchar( 100 ) not null
    );‐‐ 添加用户角色
    insert into user_roles(username,role_name) values('zhangsan','admin');
    insert into user_roles(username,role_name) values('lisi','user');
    insert into user_roles(username,role_name) values('wangwu','cmanager');
    insert into user_roles(username,role_name) values('zhaoliu','mmanager');
    insert into user_roles(username,role_name) values('chenqi','xmanager');
    
    • 权限信息
    create table roles_permissions(
    id int primary key auto_increment,
    role_name varchar( 100 ) not null,
    permission varchar( 100 ) not null
    );‐‐ 添加权限信息
    insert into roles_permissions(role_name,permission) values('admin','*');
    insert into roles_permissions(role_name,permission) values('user','sys:*:find');
    insert into roles_permissions(role_name,permission) values('cmanager','sys:c:save');
    insert into roles_permissions(role_name,permission) values('cmanager','sys:c:delete');
    insert into roles_permissions(role_name,permission) values('cmanager','sys:c:update');
    insert into roles_permissions(role_name,permission) values('cmanager','sys:c:find');
    insert into roles_permissions(role_name,permission) values('mmanager','sys:m:save');
    insert into roles_permissions(role_name,permission) values('mmanager','sys:m:delete');
    insert into roles_permissions(role_name,permission) values('mmanager','sys:m:update');
    insert into roles_permissions(role_name,permission) values('mmanager','sys:m:find');
    insert into roles_permissions(role_name,permission) values('xmanager','sys:x:save');
    insert into roles_permissions(role_name,permission) values('xmanager','sys:x:delete');
    insert into roles_permissions(role_name,permission) values('xmanager','sys:x:update');
    insert into roles_permissions(role_name,permission) values('xmanager','sys:x:find');
    

  • 2.Service层
    • UserService
@Service
public class UserService {
public void login(String userName,String userPwd) throws  Exception{Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);subject.login(token);}
}
  • 3.Controller层
    • UserController
@Controller
public class UserController {@Autowiredprivate UserService userService;@RequestMapping("/login.do")public String  login(String userName, String userPwd, Model model){try{userService.login(userName,userPwd);System.out.println("‐‐‐‐success");return "index.jsp";}catch (Exception e){System.out.println("‐‐‐‐fail");model.addAttribute("tips","登录验证失败,请重试!");return "login.jsp";}}
}
  • 4.View层
    • login.jsp
<%@ page contentType="text/html;charset=UTF‐8" language="java" %>
Title${tips}


​ - index.jsp

<%@ page contentType="text/html;charset=UTF‐8" language="java" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
Title,欢迎您![用户角色:adminusercmanagermmanagerxmanager]
C模块
  • 添加C
  • 删除C
  • 修改C
  • 查询C
X模块
  • 添加X
  • 删除X
  • 修改X
  • 查询X
M模块
  • 添加M
  • 删除M
  • 修改M
  • 查询M

5.3 SpringBoot应用整合Shiro

  • JavaSE应用中使用
  • web应用中使用
    • SSM整合Shiro(配置多,用的少)
    • SpringBoot应用整合Shiro
1. 创建SpringBoot应用
  • lombok
  • spring web
  • thymeleaf
2. 整合Druid和MyBatis
  • 依赖

    
    com.alibabadruid-spring-boot-starter1.1.10
    
    mysqlmysql-connector-java5.1.47
    
    
    org.mybatis.spring.bootmybatis-spring-boot-starter2.1.0
    
    
  • 配置

    spring:datasource:druid:url: jdbc:mysql://47.96.11.185:3306/test# MySQL如果是8.x   com.mysql.cj.jdbc.Driverdriver-class-name: com.mysql.jdbc.Driverusername: rootpassword: admin123initial-size: 1min-idle: 1max-active: 20
    mybatis:mapper-locations: classpath:mappers/*Mapper.xmltype-aliases-package: com.qfedu.springbootssm.beans
    
3. 整合Shiro
  • 导入依赖

    org.apache.shiroshiro-spring1.4.1
    
    
  • Shiro配置(java配置方式)

    • SpringBoot默认没有提供对Shiro的自动配置
    @Configuration
    public class ShiroConfig {@Beanpublic IniRealm getIniRealm(){IniRealm iniRealm = new IniRealm("classpath:shiro.ini");return iniRealm;}@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(IniRealm iniRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//securityManager要完成校验,需要realmsecurityManager.setRealm(iniRealm);return securityManager;}@Beanpublic ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();//过滤器就是shiro就行权限校验的核心,进行认证和授权是需要SecurityManager的filter.setSecurityManager(securityManager);//设置shiro的拦截规则// anon   匿名用户可访问// authc  认证用户可访问// user   使用RemeberMe的用户可访问// perms  对应权限可访问// role   对应的角色可访问Map filterMap = new HashMap<>();filterMap.put("/","anon");filterMap.put("/login.html","anon");filterMap.put("/regist.html","anon");filterMap.put("/user/login","anon");filterMap.put("/user/regist","anon");filterMap.put("/static/**","anon");filterMap.put("/**","authc");filter.setFilterChainDefinitionMap(filterMap);filter.setLoginUrl("/login.html");//设置未授权访问的页面路径filter.setUnauthorizedUrl("/login.html");return filter;}}
    
  • 认证测试

    • UserServiceImpl.java
    @Service
    public class UserServiceImpl {public void checkLogin(String userName,String userPwd) throws Exception{Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);subject.login(token);}}
    
    • UserController.java
    @Controller
    @RequestMapping("user")
    public class UserController {@Resourceprivate UserServiceImpl userService;@RequestMapping("login")public String login(String userName,String userPwd){try {userService.checkLogin(userName,userPwd);System.out.println("------登录成功!");return "index";} catch (Exception e) {System.out.println("------登录失败!");return "login";}}
    }
    
  • login.html

  Titlelogin

帐号:

密码:

六、SpringBoot应用整合Shiro—案例(JdbcRealm)

6.1 JdbcRealm介绍

在这里插入图片描述
  • 如果使用JdbcRealm,则必须提供JdbcRealm所需的表结构(权限设计)
在这里插入图片描述

6.2 JdbcRealm规定的表结构

  • 用户信息表: users

    create table users(id int primary key auto_increment,username varchar(60) not null unique,password varchar(20) not null,password_salt varchar(20)
    );insert into users(username,password) values('zhangsan','123456');
    insert into users(username,password) values('lisi','123456');
    insert into users(username,password) values('wangwu','123456');
    insert into users(username,password) values('zhaoliu','123456');
    insert into users(username,password) values('chenqi','123456');
    
  • 角色信息表: user_roles

    create table user_roles(id int primary key auto_increment,username varchar(60) not null,role_name varchar(100) not null
    );-- admin系统管理员
    -- cmanager 库管人员
    -- xmanager 销售人员
    -- kmanager 客服人员
    -- zmanager 行政人员 
    insert into user_roles(username,role_name) values('zhangsan','admin');
    insert into user_roles(username,role_name) values('lisi','cmanager');
    insert into user_roles(username,role_name) values('wangwu','xmanager');
    insert into user_roles(username,role_name) values('zhaoliu','kmanager');
    insert into user_roles(username,role_name) values('chenqi','zmanager');
  • 权限信息表:roles_permissions

    create table roles_permissions(id int primary key auto_increment,role_name varchar(100) not null,permission varchar(100) not null
    );-- 权限  sys:c:save   sys:c:delete...
    -- 管理员具备所有权限
    insert into roles_permissions(role_name,permission) values("admin","*");
    -- 库管人员
    insert into roles_permissions(role_name,permission) values("cmanager","sys:c:save");		
    insert into roles_permissions(role_name,permission) values("cmanager","sys:c:delete");
    insert into roles_permissions(role_name,permission) values("cmanager","sys:c:update");
    insert into roles_permissions(role_name,permission) values("cmanager","sys:c:find");
    -- 销售人员
    insert into roles_permissions(role_name,permission) values("xmanager","sys:c:find");
    insert into roles_permissions(role_name,permission) values("xmanager","sys:x:save");
    insert into roles_permissions(role_name,permission) values("xmanager","sys:x:delete");
    insert into roles_permissions(role_name,permission) values("xmanager","sys:x:update");
    insert into roles_permissions(role_name,permission) values("xmanager","sys:x:find");insert into roles_permissions(role_name,permission) values("xmanager","sys:k:save");
    insert into roles_permissions(role_name,permission) values("xmanager","sys:k:delete");
    insert into roles_permissions(role_name,permission) values("xmanager","sys:k:update");
    insert into roles_permissions(role_name,permission) values("xmanager","sys:k:find");
    -- 客服人员
    insert into roles_permissions(role_name,permission) values("kmanager","sys:k:find");
    insert into roles_permissions(role_name,permission) values("kmanager","sys:k:update");
    -- 新增人员
    insert into roles_permissions(role_name,permission) values("zmanager","sys:*:find");
    

6.3 SpringBoot整合Shiro

  • 创建SpringBoot应用

  • 整合Druid和MyBatis

  • 整合shiro

    • 添加依赖

      org.apache.shiroshiro-spring1.4.1
      
      
    • 配置Shiro

      @Configuration
      public class ShiroConfig {@Beanpublic JdbcRealm getJdbcRealm(DataSource dataSource){JdbcRealm jdbcRealm = new JdbcRealm();//JdbcRealm会自行从数据库查询用户及权限数据(数据库的表结构要符合JdbcRealm的规范)jdbcRealm.setDataSource(dataSource);//JdbcRealm默认开启认证功能,需要手动开启授权功能jdbcRealm.setPermissionsLookupEnabled(true);return  jdbcRealm;}@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(JdbcRealm jdbcRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(jdbcRealm);return securityManager;}@Beanpublic ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();//过滤器就是shiro就行权限校验的核心,进行认证和授权是需要SecurityManager的filter.setSecurityManager(securityManager);Map filterMap = new HashMap<>();filterMap.put("/","anon");filterMap.put("/login.html","anon");filterMap.put("/regist.html","anon");filterMap.put("/user/login","anon");filterMap.put("/user/regist","anon");filterMap.put("/static/**","anon");filterMap.put("/**","authc");filter.setFilterChainDefinitionMap(filterMap);filter.setLoginUrl("/login.html");//设置未授权访问的页面路径filter.setUnauthorizedUrl("/login.html");return filter;}}
      

    3.4 认证功能测试

6.4 Shiro的标签使用

当用户认证进入到主页面之后,需要显示用户信息及当前用户的权限信息;Shiro就提供了一套标签用于在页面来进行权限数据的呈现

  • Shiro提供了可供JSP使用的标签以及Thymeleaf中标签

    • JSP页面中引用:

      <%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
      
    • Thymeleaf模版中引用:

      • 在pom.xml文件中导入thymeleaf模版对shiro标签支持的依赖
      com.github.theborakompanionithymeleaf-extras-shiro2.0.0
      
      
      • 在ShiroConfig中配置Shiro的
      @Configuration
      public class ShiroConfig {@Beanpublic ShiroDialect getShiroDialect(){return new ShiroDialect();}//...
      }
      
      • Thymeleaf模版中引入shiro的命名空间
      ...
      
      

常用标签

七、SpringBoot整合Shiro完成权限管理案例—自定义Realm

使用JdbcRealm可以完成用户权限管理,但是我们必须提供JdbcRealm规定的数据表结构;如果在我们的项目开发中 ,这个JdbcRealm规定的数据表结构不能满足开发需求,该如何处理呢?

  • 自定义数据库表结构
  • 自定义Realm实现认证和授权
在这里插入图片描述

7.1 数据库设计

  • RBAC基于角色的访问控制

    -- 用户信息表
    create table tb_users(user_id int primary key auto_increment,username varchar(60) not null unique,password varchar(20) not null,password_salt varchar(60)
    );insert into tb_users(username,password) values('zhangsan','123456');
    insert into tb_users(username,password) values('lisi','123456');
    insert into tb_users(username,password) values('wangwu','123456');
    insert into tb_users(username,password) values('zhaoliu','123456');
    insert into tb_users(username,password) values('chenqi','123456');-- 角色信息表
    create table tb_roles(role_id int primary key auto_increment,role_name varchar(60) not null
    );insert into tb_roles(role_name) values('admin');
    insert into tb_roles(role_name) values('cmanager');  -- 仓管
    insert into tb_roles(role_name) values('xmanager');  --  销售
    insert into tb_roles(role_name) values('kmanager');  -- 客服
    insert into tb_roles(role_name) values('zmanager');  -- 行政-- 权限信息表
    create table tb_permissions(permission_id int primary key auto_increment,		-- 1permission_code varchar(60) not null,						-- sys:c:findpermission_name varchar(60)											-- 仓库查询
    );
    insert into tb_permissions(permission_code,permission_name) values('sys:c:save','入库');
    insert into tb_permissions(permission_code,permission_name) values('sys:c:delete','出库');
    insert into tb_permissions(permission_code,permission_name) values('sys:c:update','修改');
    insert into tb_permissions(permission_code,permission_name) values('sys:c:find','查询');insert into tb_permissions(permission_code,permission_name) values('sys:x:save','新增订单');
    insert into tb_permissions(permission_code,permission_name) values('sys:x:delete','删除订单');
    insert into tb_permissions(permission_code,permission_name) values('sys:x:update','修改订单');
    insert into tb_permissions(permission_code,permission_name) values('sys:x:find','查询订单');insert into tb_permissions(permission_code,permission_name) values('sys:k:save','新增客户');
    insert into tb_permissions(permission_code,permission_name) values('sys:k:delete','删除客户');
    insert into tb_permissions(permission_code,permission_name) values('sys:k:update','修改客户');
    insert into tb_permissions(permission_code,permission_name) values('sys:k:find','查询客户');-- 用户角色表
    create table tb_urs(uid int not null,rid int not null-- primary key(uid,rid),-- constraint FK_user foreign key(uid) references tb_users(user_id),-- constraint FK_role foreign key(rid) references tb_roles(role_id)
    );
    insert into tb_urs(uid,rid) values(1,1);
    insert into tb_urs(uid,rid) values(1,2);
    insert into tb_urs(uid,rid) values(1,3);
    insert into tb_urs(uid,rid) values(1,4);
    insert into tb_urs(uid,rid) values(1,5);insert into tb_urs(uid,rid) values(2,2);
    insert into tb_urs(uid,rid) values(3,3);
    insert into tb_urs(uid,rid) values(4,4);
    insert into tb_urs(uid,rid) values(5,5);-- 角色权限表
    create table tb_rps(rid int not null,pid int not null
    );
    -- 给仓管角色分配权限
    insert into tb_rps(rid,pid) values(2,1);
    insert into tb_rps(rid,pid) values(2,2);
    insert into tb_rps(rid,pid) values(2,3);
    insert into tb_rps(rid,pid) values(2,4);
    -- 给销售角色分配权限
    insert into tb_rps(rid,pid) values(3,4);
    insert into tb_rps(rid,pid) values(3,5);
    insert into tb_rps(rid,pid) values(3,6);
    insert into tb_rps(rid,pid) values(3,7);
    insert into tb_rps(rid,pid) values(3,8);
    insert into tb_rps(rid,pid) values(3,9);
    insert into tb_rps(rid,pid) values(3,10);
    insert into tb_rps(rid,pid) values(3,11);
    insert into tb_rps(rid,pid) values(3,12);
    -- 给客服角色分配权限
    insert into tb_rps(rid,pid) values(4,11);
    insert into tb_rps(rid,pid) values(4,12);
    -- 给行政角色分配权限
    insert into tb_rps(rid,pid) values(5,4);
    insert into tb_rps(rid,pid) values(5,8);
    insert into tb_rps(rid,pid) values(5,12);
    

7.2 DAO实现

  • Shiro进行认证需要用户信息:

    • 根据用户名查询用户信息
  • Shiro进行授权管理需要当前用户的角色和权限

    • 根据用户名查询当前用户的角色列表(3张表连接查询)

    • 根据用户名查询当前用户的权限列表(5张表连接查询)

7.2.1 创建SpringBoot项目,整合MyBatis
7.2.2 根据用户名查询用户信息
  • 创建BeanBean
@Data
public class User {private Integer userId;private String userName;private String userPwd;private String pwdSalt;
}
  • 创建DAO
public interface UserDAO {public User queryUserByUsername(String username) throws  Exception;
}
  • 映射配置



7.2.3 根据用户名查询角色名列表
  • 创建DAO
public interface RoleDAO {public Set  queryRoleNamesByUsername(String username) throws Exception;
}
  • 映射配置



7.2.4 根据用户名查询权限列表
  • 创建DAO
public interface PermissionDAO {public Set queryPermissionsByUsername(String  username) throws Exception;
}
  • 映射配置



7.3 整合Shiro

  • 导入依赖
org.apache.shiroshiro-spring1.4.1

com.github.theborakompanionithymeleaf-extras-shiro2.0.0

  • 配置Shiro-基于Java配置方式
@Configuration
public class ShiroConfig {@Beanpublic ShiroDialect getShiroDialect(){return new ShiroDialect();}//自定义Realm@Beanpublic MyRealm getMyRealm(){MyRealm myRealm = new MyRealm();return myRealm;}@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);return securityManager;}@Beanpublic ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();//过滤器就是shiro就行权限校验的核心,进行认证和授权是需要SecurityManager的filter.setSecurityManager(securityManager);Map filterMap = new HashMap<>();filterMap.put("/","anon");filterMap.put("/index.html","anon");filterMap.put("/login.html","anon");filterMap.put("/regist.html","anon");filterMap.put("/user/login","anon");filterMap.put("/user/regist","anon");filterMap.put("/layui/**","anon");filterMap.put("/**","authc");filter.setFilterChainDefinitionMap(filterMap);filter.setLoginUrl("/login.html");//设置未授权访问的页面路径()filter.setUnauthorizedUrl("/login.html");return filter;}}
  • 自定义Realm
/*** 1.创建一个类继承AuthorizingRealm类(实现了Realm接口的类)* 2.重写doGetAuthorizationInfo和doGetAuthenticationInfo方法* 3.重写getName方法返回当前realm的一个自定义名称*/
public class MyRealm extends AuthorizingRealm {@Resourceprivate UserDAO userDAO;@Resourceprivate RoleDAO roleDAO;@Resourceprivate PermissionDAO permissionDAO;public String getName() {return "myRealm";}/*** 获取授权数据(将当前用户的角色及权限信息查询出来)*/protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {//获取用户的用户名String username = (String) principalCollection.iterator().next();//根据用户名查询当前用户的角色列表Set roleNames = roleDAO.queryRoleNamesByUsername(username);//根据用户名查询当前用户的权限列表Set ps = permissionDAO.queryPermissionsByUsername(username);SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setRoles(roleNames);info.setStringPermissions(ps);return info;}/*** 获取认证的安全数据(从数据库查询的用户的正确数据)*/protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//参数authenticationToken就是传递的  subject.login(token)// 从token中获取用户名UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//根据用户名,从数据库查询当前用户的安全数据User user = userDAO.queryUserByUsername(username);AuthenticationInfo info = new SimpleAuthenticationInfo(username,           //当前用户用户名user.getUserPwd(),   //从数据库查询出来的安全密码getName());return info;}
}

7.4 SpringBoot应用打包部署

  • SpringBoot项目集成了web容器(Tomcat),所以SpringBoot应用是可以打包成jar直接运行的

八、加密加盐

  • 明文-----(加密规则)-----密文
  • 加密规则可以自定义,在项目开发中我们通常使用BASE64和MD5编码方式
    • BASE64:可反编码的编码方式(对称)
      • 明文----密文
      • 密文----明文
    • MD5: 不可逆的编码方式(非对称)
      • 明文----密文
  • 如果数据库用户的密码存储的密文,Shiro该如何完成验证呢?
  • 使用Shiro提供的加密功能,对输入的密码进行加密之后再进行认证。

8.1 加密介绍

在这里插入图片描述

8.2 Shiro使用加密认证

  • 配置matcher

    @Configuration
    public class ShiroConfig {//...@Beanpublic HashedCredentialsMatcher getHashedCredentialsMatcher(){HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();//matcher就是用来指定加密规则//加密方式matcher.setHashAlgorithmName("md5");//hash次数matcher.setHashIterations(1);	//此处的循环次数要与用户注册是密码加密次数一致return matcher;}//自定义Realm@Beanpublic MyRealm getMyRealm( HashedCredentialsMatcher matcher ){MyRealm myRealm = new MyRealm();myRealm.setCredentialsMatcher(matcher);return myRealm;}//...
    }

8.3 用户注册密码加密处理

  • registh.html

    帐号:

    密码:

  • UserController

    @Controller
    @RequestMapping("user")
    public class UserController {@Resourceprivate UserServiceImpl userService;@RequestMapping("/regist")public String regist(String userName,String userPwd) {System.out.println("------注册");//注册的时候要对密码进行加密存储Md5Hash md5Hash = new Md5Hash(userPwd);System.out.println("--->>>"+ md5Hash.toHex());//加盐加密int num = new Random().nextInt(90000)+10000;   //10000—99999String salt = num+"";Md5Hash md5Hash2 = new Md5Hash(userPwd,salt);System.out.println("--->>>"+md5Hash2);//加盐加密+多次hashMd5Hash md5Hash3 = new Md5Hash(userPwd,salt,3);System.out.println("--->>>"+md5Hash3);//SimpleHash hash = new SimpleHash("md5",userPwd,num,3);//将用户信息保存到数据库时,保存加密后的密码,如果生成的随机盐,盐也要保存return "login";}}
    

8.4 如果密码进行了加盐处理,则Realm在返回认证数据时需要返回盐

  • 在自定义Realm中:

    public class MyRealm extends AuthorizingRealm {@Resourceprivate UserDAO userDAO;@Resourceprivate RoleDAO roleDAO;@Resourceprivate PermissionDAO permissionDAO;public String getName() {return "myRealm";}/*** 获取认证的安全数据(从数据库查询的用户的正确数据)*/protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//参数authenticationToken就是传递的  subject.login(token)// 从token中获取用户名UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//根据用户名,从数据库查询当前用户的安全数据User user = userDAO.queryUserByUsername(username);//        AuthenticationInfo info = new SimpleAuthenticationInfo(
    //                username,           //当前用户用户名
    //                user.getUserPwd(),   //从数据库查询出来的安全密码
    //                getName());//如果数据库中用户的密码是加了盐的AuthenticationInfo info = new SimpleAuthenticationInfo(username,           //当前用户用户名user.getUserPwd(),   //从数据库查询出来的安全密码ByteSource.Util.bytes(user.getPwdSalt()),getName());return info;}
    }
    

九、退出登录

  • 在Shiro过滤器中进行配置,配置logut对应的路径

    filterMap.put("/exit","logout");
    
  • 在页面的“退出”按钮上,跳转到logout对应的url

    退出
    

十、授权管理

用户登录成功之后,要进行响应的操作就需要有对应的权限;在进行操作之前对权限进行检查—授权

权限控制通常有两类做法:

  • 不同身份的用户登录,我们现在不同的操作菜单(没有权限的菜单不现实)
  • 对所有用户显示所有菜单,当用户点击菜单以后再验证当前用户是否有此权限,如果没有则提示权限不足

10.1 HTML授权

  • 在菜单页面只显示当前用户拥有权限操作的菜单

  • shiro标签

    入库

10.2 过滤器授权

  • 在shiro过滤器中对请求的url进行权限设置

    filterMap.put("/c_add.html","perms[sys:c:save]");//设置未授权访问的页面路径—当权限不足时显示此页面
    filter.setUnauthorizedUrl("/lesspermission.html");
    

10.3 注解授权

  • 配置Spring对Shiro注解的支持:ShiroConfig.java

    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();autoProxyCreator.setProxyTargetClass(true);return autoProxyCreator;
    }@Bean
    public AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor( DefaultWebSecurityManager securityManager){AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;
    }
    
  • 在请求的控制器添加权限注解

    @Controller
    @RequestMapping("customer")
    public class CustomerController {@RequestMapping("list")//如果没有 sys:k:find 权限,则不允许执行此方法@RequiresPermissions("sys:k:find")//    @RequiresRoles("")public String list(){System.out.println("----------->查询客户信息");return "customer_list";}}
    
  • 通过全局异常处理,指定权限不足时的页面跳转

    @ControllerAdvice
    public class GlobalExceptionHandler {@ExceptionHandlerpublic String doException(Exception e){if(e instanceof AuthorizationException){return  "lesspermission";}return null;}}
    

10.4 手动授权

  • 在代码中进行手动的权限校验

    Subject subject = SecurityUtils.getSubject();
    if(subject.isPermitted("sys:k:find")){System.out.println("----------->查询客户信息");return "customer_list";
    }else{return "lesspermission";
    }
    

十一、缓存使用

使用Shiro进行权限管理过程中,每次授权都会访问realm中的doGetAuthorizationInfo方法查询当前用户的角色及权限信息,如果系统的用户量比较大则会对数据库造成比较大的压力

Shiro支持缓存以降低对数据库的访问压力(缓存的是授权信息)

11.1 导入依赖

org.springframework.bootspring-boot-starter-cache
net.sf.ehcacheehcache
org.apache.shiroshiro-ehcache1.4.0

11.2 配置缓存策略

  • 在resources目录下创建一个xml文件(ehcache.xml)



11.3 加入缓存管理

  • ShiroConfig.java
@Bean
public EhCacheManager getEhCacheManager(){EhCacheManager ehCacheManager = new EhCacheManager();ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");return ehCacheManager;
}@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);securityManager.setCacheManager(getEhCacheManager());return securityManager;
}

十二、session管理

Shiro进行认证和授权是基于session实现的,Shiro包含了对session的管理

  • 如果我们需要对session进行管理

    • 自定义session管理器
    • 将自定义的session管理器设置给SecurityManager
  • 配置自定义SessionManager:ShiroConfig.java

    @Bean
    public DefaultWebSessionManager getDefaultWebSessionManager(){DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();System.out.println("----------"+sessionManager.getGlobalSessionTimeout()); // 1800000//配置sessionManagersessionManager.setGlobalSessionTimeout(5*60*1000);return sessionManager;
    }@Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);securityManager.setCacheManager(getEhCacheManager());securityManager.setSessionManager(getDefaultWebSessionManager());return securityManager;
    }
    

解疑

  • 如何避免登录页面显示在页面框架中?

  • 在登录页面添如下JS代码:

    
    

十三、RememberMe

将用户对页面访问的权限分为三个级别:

  • 未认证—可访问的页面—(陌生人)—问候
  • login.html、regist.html
  • 记住我—可访问的页面—(前女友)—朋友间的拥抱
  • info.html
  • 已认证—可访问的页面—(现女友)—牵手
  • 转账.html

13.1 在过滤器中设置“记住我”可访问的url

// anon     表示未认证可访问的url
// user     表示记住我可访问的url(已认证也可以访问)
//authc     表示已认证可访问的url
//perms		表示必须具备指定的权限才可访问
//logout	表示指定退出的url
filterMap.put("/","anon");
filterMap.put("/index.html","user");
filterMap.put("/login.html","anon");
filterMap.put("/regist.html","anon");
filterMap.put("/user/login","anon");
filterMap.put("/user/regist","anon");
filterMap.put("/layui/**","anon");
filterMap.put("/**","authc");
filterMap.put("/c_add.html","perms[sys:c:save]");
filterMap.put("/exit","logout");

13.2 在ShiroConfig.java中配置基于cookie的rememberMe管理器

@Bean
public CookieRememberMeManager cookieRememberMeManager(){CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();//cookie必须设置nameSimpleCookie cookie = new SimpleCookie("rememberMe");cookie.setMaxAge(30*24*60*60);rememberMeManager.setCookie(cookie);return  rememberMeManager;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(myRealm);securityManager.setCacheManager(getEhCacheManager());securityManager.setSessionManager(getDefaultWebSessionManager());//设置remember管理器securityManager.setRememberMeManager(cookieRememberMeManager());return securityManager;
}

13.3 登录认证时设置token“记住我”

  • 登录页面

帐号:

密码:

记住我:

  • 控制器
@Controller
@RequestMapping("user")
public class UserController {@Resourceprivate UserServiceImpl userService;@RequestMapping("login")public String login(String userName,String userPwd,boolean rememberMe){try {userService.checkLogin(userName,userPwd,rememberMe);System.out.println("------登录成功!");return "index";} catch (Exception e) {System.out.println("------登录失败!");return "login";}}//...
}
  • service
@Service
public class UserServiceImpl {public void checkLogin(String userName, String userPwd,boolean rememberMe) throws Exception {//Shiro进行认证 ——入口Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);token.setRememberMe(rememberMe);subject.login(token);}
}

十四、Shiro多Realm配置

14.1 使用场景

  • 当shiro进行权限管理,数据来自于不同的数据源时,我们可以给SecurityManager配置多个Realm

14.2 多个Realm的处理方式

14.2.1 链式处理
  • 多个Realm依次进行认证
14.2.2 分支处理
  • 根据不同的条件从多个Realm中选择一个进行认证处理

14.3 多Realm配置(链式处理)

  • 定义多个Realm

    • UserRealm

      public class UserRealm extends AuthorizingRealm {Logger logger = LoggerFactory.getLogger(UserRealm.class);@Overridepublic String getName() {return "UserRealm";}@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {logger.info("--------------------------------UserRealm");//从token中获取usernameUsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//根据username从users表中查询用户信息SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,"123456",getName());return info;}
      }
      
    • ManagerRealm

      public class ManagerRealm extends AuthorizingRealm {Logger logger = LoggerFactory.getLogger(ManagerRealm.class);@Overridepublic String getName() {return "ManagerRealm";}@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {return null;}@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {logger.info("--------------------------------ManagerRealm");//从token中获取usernameUsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//根据username从吗managers表中查询用户信息SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,"222222",getName());return info;}
      }
      
  • 在ShiroConfig.java中为SecurityManager配置多个Realm

    @Configuration
    public class ShiroConfig {@Beanpublic UserRealm userRealm(){UserRealm userRealm = new UserRealm();return  userRealm;}@Beanpublic ManagerRealm managerRealm(){ManagerRealm managerRealm = new ManagerRealm();return managerRealm;}@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//securityManager中配置多个realmCollection realms = new ArrayList<>();realms.add(userRealm());realms.add(managerRealm());securityManager.setRealms(realms);return securityManager;}//...}
  • 测试代码:

    • login.html

      帐号:

      密码:

      普通用户管理员

    • UserController.java

      @Controller
      @RequestMapping("user")
      public class UserController {Logger logger = LoggerFactory.getLogger(UserController.class);@RequestMapping("login")public String login(String userName,String userPwd, String loginType){logger.info("~~~~~~~~~~~~~UserController-login");try{UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);Subject subject = SecurityUtils.getSubject();subject.login(token);return "index";}catch (Exception e){return "login";}}}
      

14.4 Shiro认证处理源码分析

在这里插入图片描述
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {this.assertRealmsConfigured();Collection realms = this.getRealms();// this.doMultiRealmAuthentication(realms, authenticationToken);中的realms参数就是认证会执行的Realmreturn realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);}

14.5 多Realm配置(分支处理)

根据不同的条件执行不同的Realm

  • 流程分析

在这里插入图片描述

  • 实现案例:用户不同身份登录执行不同的Realm

    • 自定义Realm(UserRealm\ManagerRealm)

      • 当在登录页面选择“普通用户”登录,则执行UserRealm的认证
      • 当在登录页面选择“管理员”登录,则执行ManagerRealm的认证
    • Realm的声明及配置

    • 自定义Token

      public class MyToken extends UsernamePasswordToken {private String loginType;public MyToken(String userName,String userPwd, String loginType) {super(userName,userPwd);this.loginType = loginType;}public String getLoginType() {return loginType;}public void setLoginType(String loginType) {this.loginType = loginType;}
      }
      
    • 自定义认证器

      public class MyModularRealmAuthenticator extends ModularRealmAuthenticator {Logger logger = LoggerFactory.getLogger(MyModularRealmAuthenticator.class);@Overrideprotected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {logger.info("------------------------------MyModularRealmAuthenticator");this.assertRealmsConfigured();Collection realms = this.getRealms();MyToken token = (MyToken) authenticationToken;String loginType = token.getLoginType(); // Userlogger.info("------------------------------loginType:"+loginType);Collection typeRealms = new ArrayList<>();for(Realm realm:realms){if(realm.getName().startsWith(loginType)){  //UserRealmtypeRealms.add(realm);}}if(typeRealms.size()==1){return this.doSingleRealmAuthentication((Realm)typeRealms.iterator().next(), authenticationToken);}else{return this.doMultiRealmAuthentication(typeRealms, authenticationToken);}}}
      
    • 配置自定义认证器

      @Configuration
      public class ShiroConfig {@Beanpublic UserRealm userRealm(){UserRealm userRealm = new UserRealm();return  userRealm;}@Beanpublic ManagerRealm managerRealm(){ManagerRealm managerRealm = new ManagerRealm();return managerRealm;}@Beanpublic MyModularRealmAuthenticator myModularRealmAuthenticator(){MyModularRealmAuthenticator myModularRealmAuthenticator = new MyModularRealmAuthenticator();return myModularRealmAuthenticator;}@Beanpublic DefaultWebSecurityManager getDefaultWebSecurityManager(){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();//配置自定义认证器(放在realms设置之前)securityManager.setAuthenticator(myModularRealmAuthenticator());//securityManager中配置多个realmCollection realms = new ArrayList<>();realms.add(userRealm());realms.add(managerRealm());securityManager.setRealms(realms);return securityManager;}//...}
      
    • 测试:控制器接受数据进行认证

      • login.html

      帐号:

      密码:

      普通用户管理员

      • UserController.java
      @Controller
      @RequestMapping("user")
      public class UserController {Logger logger = LoggerFactory.getLogger(UserController.class);@RequestMapping("login")public String login(String userName,String userPwd, String loginType){logger.info("~~~~~~~~~~~~~UserController-login");try{//UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);MyToken token = new MyToken(userName,userPwd,loginType);Subject subject = SecurityUtils.getSubject();subject.login(token);return "index";}catch (Exception e){return "login";}}}
      

附件 单体项目开发技术清单

1. JSP应用

  • View JSP(Java Server Page)

  • Control Servlet

  • Model JDBC(Java Database Connection)

  • 第一阶段的项目:JSP/Servlet+JDBC

2. SSM

  • View JSP
  • Control SpringMVC
  • Model MyBatis
  • 第二阶段项目:JSP+SSM

3. SpringBoot

  • View Thymeleaf
  • Control SpringMVC
  • Model MyBatis/tkMapper
  • 第三阶段练习项目:thymeleaf+SpringBoot(SSM)

单体项目:项目的前端页面与服务端的代码在同一个项目中(部署在同一个服务器上)

在这里插入图片描述

相关内容

热门资讯

【实验报告】实验一 图像的... 实验目的熟悉Matlab图像运算的基础——矩阵运算;熟悉图像矩阵的显示方法࿰...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
大模型落地比趋势更重要,NLP... 全球很多人都开始相信,以ChatGPT为代表的大模型,将带来一场NLP领...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
kuernetes 资源对象分... 文章目录1. pod 状态1.1 容器启动错误类型1.2 ImagePullBackOff 错误1....
STM32实战项目-数码管 程序实现功能: 1、上电后,数码管间隔50ms计数; 2、...
TM1638和TM1639差异... TM1638和TM1639差异说明 ✨本文不涉及具体的单片机代码驱动内容,值针对芯...
Qt+MySql开发笔记:Qt... 若该文为原创文章,转载请注明原文出处 本文章博客地址:https://h...
Java内存模型中的happe... 第29讲 | Java内存模型中的happen-before是什么? Java 语言...
《扬帆优配》算力概念股大爆发,... 3月22日,9股封单金额超亿元,工业富联、鸿博股份、鹏鼎控股分别为3.0...
CF1763D Valid B... CF1763D Valid Bitonic Permutations 题目大意 拱形排列࿰...
SQL语法 DDL、DML、D... 文章目录1 SQL通用语法2 SQL分类3 DDL 数据定义语言3.1 数据库操作3.2 表操作3....
文心一言 VS ChatGPT... 3月16号,百度正式发布了『文心一言』,这是国内公司第一次发布类Chat...
CentOS8提高篇5:磁盘分...        首先需要在虚拟机中模拟添加一块新的硬盘设备,然后进行分区、格式化、挂载等...
Linux防火墙——SNAT、... 目录 NAT 一、SNAT策略及作用 1、概述 SNAT应用环境 SNAT原理 SNAT转换前提条...
部署+使用集群的算力跑CPU密... 我先在开头做一个总结,表达我最终要做的事情和最终环境是如何的,然后我会一...
Uploadifive 批量文... Uploadifive 批量文件上传_uploadifive 多个上传按钮_asing1elife的...
C++入门语法基础 文章目录:1. 什么是C++2. 命名空间2.1 域的概念2.2 命名...
2023年全国DAMA-CDG... DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义...
php实现助记词转TRX,ET... TRX助记词转地址网上都是Java,js或其他语言开发的示例,一个简单的...
【分割数据集操作集锦】毕设记录 1. 按要求将CSV文件转成json文件 有时候一些网络模型的源码会有data.json这样的文件里...
Postman接口测试之断言 如果你看文字部分还是不太理解的话,可以看看这个视频,详细介绍postma...
前端学习第三阶段-第4章 jQ... 4-1 jQuery介绍及常用API导读 01-jQuery入门导读 02-JavaScri...
4、linux初级——Linu... 目录 一、用CRT连接开发板 1、安装CRT调试工具 2、连接开发板 3、开机后ctrl+c...
Urban Radiance ... Urban Radiance Fields:城市辐射场 摘要:这项工作的目标是根据扫描...
天干地支(Java) 题目描述 古代中国使用天干地支来记录当前的年份。 天干一共有十个,分别为:...
SpringBoot雪花ID长... Long类型精度丢失 最近项目中使用雪花ID作为主键,雪花ID是19位Long类型数...
对JSP文件的理解 JSP是java程序。(JSP本质还是一个Servlet) JSP是&#...
【03173】2021年4月高... 一、单向填空题1、大量应用软件开发工具,开始于A、20世纪70年代B、20世纪 80年...
LeetCode5.最长回文子... 目录题目链接题目分析解题思路暴力中心向两边拓展搜索 题目链接 链接 题目分析 简单来说࿰...