springboot权限验证学习-上

server/2024/9/20 3:58:07/ 标签: spring boot, 学习, 后端, 权限

创建maven项目

创建父工程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这类项目和原来项目的区别在于,打包方式是pom
由于pom项目一般都是用来做父项目的,所以该项目的src文件夹可以删除掉。

创建子工程

在这里插入图片描述
子工程pom.xml
在这里插入图片描述
父工程pom.xml
在这里插入图片描述

添加依赖

父工程导入依赖包

<!--导入springboot 父工程-->
<parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.5.3</version>
</parent>

子工程添加依赖包

<dependencies><!--web依赖包, web应用必备--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--MySQL,连接MySQL必备--><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><version>8.0.33</version></dependency><!--MyBatis-plus,ORM框架,访问并操作数据库--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope></dependency>
</dependencies>

stu01下main下新建resources文件夹,该文件夹下新建application.yml

server:port: 8080
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/javaauth01?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8username: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:mapper-locations: classpath:mapper/*.xml                #对应mapper映射xml文件所在路径type-aliases-package: com.wujialiang.auth.entity          #对应实体类路径

resources下新建mapper

修改app.java

package com.wujialiang.auth;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;/*** 网站入口**/
@SpringBootApplication
public class App {public static void main(String[] args) {// 第一个参数是该类的名字.class 第二个参数是main方法中的参数SpringApplication.run(App.class, args);}
}

jwt登录验证

实现登录

entity下新建User实体

package com.wujialiang.auth.entity;import lombok.Data;@Data
public class User{private Integer id;private String username;private String password;
}

新建SQL

create table User(Id int primary key AUTO_INCREMENT,UserName varchar(50),Password varchar(50)
);insert into User (username, password) values('admin','admin');
insert into User (username, password) values('wjl','xiaoming');

在这里插入图片描述

新建mapper文件夹,新建UserMapper接口

package com.wujialiang.auth.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wujialiang.auth.entity.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;//@MapperScan("com.example.playspring.mapper")
@Repository
@Mapper
public interface UserMapper extends BaseMapper<User> {/*** 用户登录判断* @param userName* @param password* @return*/boolean userLogin(@Param("userName") String userName,@Param("password") String password);
}

resource下mapper下新建UserMapper.xml

<?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.wujialiang.auth.mapper.UserMapper"><select id="userLogin" resultType="java.lang.Boolean">select count(id) from User where username=#{userName} and Password=#{password}</select>
</mapper>

新建UserController

package com.wujialiang.auth.controller;import com.wujialiang.auth.entity.User;
import com.wujialiang.auth.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class UserController {@Autowiredprivate UserMapper userMapper;@PostMapping("/login")public String login(@RequestBody User user) {boolean isLogin = userMapper.userLogin(user.getUsername(), user.getPassword());if (isLogin) {return "登录成功";}return "账号密码错误";}
}

访问接口调试http://localhost:8080/login
在这里插入图片描述

接入jwt

添加maven包

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version>
</dependency>

新建JwtUtil.java

package com.wujialiang.auth.util;import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;import java.time.Duration;
import java.util.Date;/*** jwt帮助类*/
public class JwtUtil {/*** 这个秘钥是防止JWT被篡改的关键,随便写什么都好,但决不能泄露*/private final static String secretKey = "1234567890";/*** 过期时间目前设置成2天,这个配置随业务需求而定*/private final static Duration expiration = Duration.ofHours(2);/*** 生成JWT** @param userName 用户名* @return JWT*/public static String generate(String userName) {// 过期时间Date expiryDate = new Date(System.currentTimeMillis() + expiration.toMillis());return Jwts.builder().setSubject(userName) // 将userName放进JWT.setIssuedAt(new Date()) // 设置JWT签发时间.setExpiration(expiryDate)  // 设置过期时间.signWith(SignatureAlgorithm.HS512, secretKey) // 设置加密算法和秘钥.compact();}/*** 解析JWT** @param token JWT字符串* @return 解析成功返回Claims对象,解析失败返回null*/public static Claims parse(String token) {// 如果是空字符串直接返回nullif (StringUtils.isEmpty(token)) {return null;}// 这个Claims对象包含了许多属性,比如签发时间、过期时间以及存放的数据等Claims claims = null;// 解析失败了会抛出异常,所以我们要捕捉一下。token过期、token非法都会导致解析失败try {claims = Jwts.parser().setSigningKey(secretKey) // 设置秘钥.parseClaimsJws(token).getBody();} catch (JwtException e) {// 这里应该用日志输出,为了演示方便就直接打印了System.err.println("解析失败!");}return claims;}
}

修改登录接口

@RestController
public class UserController {@Autowiredprivate UserMapper userMapper;@PostMapping("/login")public String login(@RequestBody User user) {boolean isLogin = userMapper.userLogin(user.getUsername(), user.getPassword());if (isLogin) {// 如果正确的话就返回生成的token(注意哦,这里服务端是没有存储任何东西的)return JwtUtil.generate(user.getUsername());}return "账号密码错误";}
}

测试登录,返回token,后面要用
在这里插入图片描述

编写需要登陆后才能获取的接口

@GetMapping("/jwttest")
public String api(HttpServletRequest request) {// 从请求头中获取token字符串String jwt = request.getHeader("Authorization");// 解析失败就提示用户登录if (JwtUtil.parse(jwt) == null) {return "请先登录";}// 解析成功就执行业务逻辑返回数据return "api成功返回数据";
}

没有token
在这里插入图片描述
请求头中添加token
在这里插入图片描述

拦截器统一处理用户是否登录

新建LoginInterceptor

package com.wujialiang.auth.interceptor;import com.wujialiang.auth.util.JwtUtil;
import io.jsonwebtoken.Claims;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;/*** 登录拦截器*/
public class LoginInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 简单的白名单,登录这个接口直接放行if ("/login".equals(request.getRequestURI())) {return true;}// 从请求头中获取token字符串并解析Claims claims = JwtUtil.parse(request.getHeader("Authorization"));// 已登录就直接放行if (claims != null) {return true;}// 走到这里就代表是其他接口,且没有登录// 设置响应数据类型为json(前后端分离)response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();// 设置响应内容,结束请求out.write("请先登录");out.flush();out.close();return false;}
}

拦截器类写好之后,别忘了要使其生效,这里我们直接让SpringBoot启动类实现WevMvcConfigurer接口来做:

package com.wujialiang.auth;import com.wujialiang.auth.interceptor.LoginInterceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 网站入口**/
@SpringBootApplication
public class App implements WebMvcConfigurer {public static void main(String[] args)  {// 第一个参数是该类的名字.class 第二个参数是main方法中的参数SpringApplication.run(App.class, args);}@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 使拦截器生效registry.addInterceptor(new LoginInterceptor());}
}

修改jwttest接口

@GetMapping("/jwttest")
public String api() {return "api成功返回数据";
}

这样接口就简洁多了
在这里插入图片描述
在这里插入图片描述

上下文对象

为了方便在其他地方获取用户信息,特别是service层
首先我们定义一个上下文类,这个类专门存储JWT解析出来的用户信息。我们要用到ThreadLocal,以防止线程冲突

ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景

package com.wujialiang.auth.context;public final class UserContext {private static final ThreadLocal<String> user = new ThreadLocal<String>();public static void add(String userName) {user.set(userName);}public static void remove() {user.remove();}/*** @return 当前登录用户的用户名*/public static String getCurrentUserName() {return user.get();}
}

这个类创建好之后我们还需要在拦截器里做下处理

// 从请求头中获取token字符串并解析
Claims claims = JwtUtil.parse(request.getHeader("Authorization"));
// 已登录就直接放行
if (claims != null) {// 将我们之前放到token中的userName给存到上下文对象中UserContext.add(claims.getSubject());return true;
}

在这里插入图片描述
新建UserService

package com.wujialiang.auth.service;import com.wujialiang.auth.context.UserContext;
import com.wujialiang.auth.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;public Boolean userLogin(String userName,String password){boolean isLogin = userMapper.userLogin(userName, password);return isLogin;}public void doSomething() {String currentUserName = UserContext.getCurrentUserName();System.out.println("Service层---当前用户登录名:" + currentUserName);}
}

修改UserController

package com.wujialiang.auth.controller;import com.wujialiang.auth.entity.User;
import com.wujialiang.auth.service.UserService;
import com.wujialiang.auth.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;@RestController
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/login")public String login(@RequestBody User user) {boolean isLogin = userService.userLogin(user.getUsername(), user.getPassword());if (isLogin) {// 如果正确的话就返回生成的token(注意哦,这里服务端是没有存储任何东西的)return JwtUtil.generate(user.getUsername());}return "账号密码错误";}@GetMapping("/jwttest")public String api() {userService.doSomething();return "api成功返回数据";}
}

在这里插入图片描述
至此jwt登录验证完成

权限控制

页面权限

页面权限非常容易理解,就是有这个权限的用户才能访问这个页面,没这个权限的用户就无法访问,它是以整个页面为维度,对权限的控制并没有那么细,所以是一种粗颗粒权限
最直观的一个例子就是,有权限的用户就会显示所有菜单,无权限的用户就只会显示部分菜单:
资源表设计

create table resource(id int primary key AUTO_INCREMENT comment '主键',userId int comment '用户id',path varchar(255) comment 'URL路径'
);insert into resource (userId, path) value(1,'/');
insert into resource (userId, path) value(1,'/user/account');
insert into resource (userId, path) value(1,'/user/role');
insert into resource (userId, path) value(1,'/data');
insert into resource (userId, path) value(2,'/user/role');
insert into resource (userId, path) value(2,'/data');

用户1有全部页面的权限,用户2有部分页面的权限
新建Resource

package com.wujialiang.auth.entity;import lombok.Data;@Data
public class Resource {private int id;private int userId;private String path;
}

新建ResouceMapper

package com.wujialiang.auth.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wujialiang.auth.entity.Resource;import java.util.List;@Repository
@Mapper
public interface ResouceMapper extends BaseMapper<Resource> {/*** 获取用户的菜单* @param userName* @return*/List<String> getCurrentUserMenus(String userName);
}

新建ResouceMapper.xml

<?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.wujialiang.auth.mapper.ResouceMapper"><select id="getCurrentUserMenus" parameterType="java.lang.String" resultType="java.lang.String">select path from resource where userId = (select user.id from user  where UserName=#{userName} limit 1)</select>
</mapper>

修改UserService

package com.wujialiang.auth.service;import com.wujialiang.auth.context.UserContext;
import com.wujialiang.auth.mapper.ResouceMapper;
import com.wujialiang.auth.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class UserService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate ResouceMapper resouceMapper;public Boolean userLogin(String userName,String password){boolean isLogin = userMapper.userLogin(userName, password);return isLogin;}public void doSomething() {String currentUserName = UserContext.getCurrentUserName();System.out.println("Service层---当前用户登录名:" + currentUserName);}/*** 获取当前用户的菜单* @return*/public List<String> getCurrentUserMenus(){String currentUserName = UserContext.getCurrentUserName();return resouceMapper.getCurrentUserMenus(currentUserName);}
}

修改UserController

package com.wujialiang.auth.controller;import com.wujialiang.auth.entity.User;
import com.wujialiang.auth.service.UserService;
import com.wujialiang.auth.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;import java.util.List;@RestController
public class UserController {@Autowiredprivate UserService userService;/*** 登录接口* @param user* @return*/@PostMapping("/login")public String login(@RequestBody User user) {boolean isLogin = userService.userLogin(user.getUsername(), user.getPassword());if (isLogin) {// 如果正确的话就返回生成的token(注意哦,这里服务端是没有存储任何东西的)return JwtUtil.generate(user.getUsername());}return "账号密码错误";}@GetMapping("/jwttest")public String api() {userService.doSomething();return "api成功返回数据";}/*** 获取当前用户的菜单* @return*/@GetMapping("/menus")public List<String> getMenus() {return userService.getCurrentUserMenus();}
}

用户1获取
在这里插入图片描述
用户2获取菜单接口
在这里插入图片描述
对于页面的增删改不做讲解

RBAC模型

我这里为了方便演示所以没有设置过多的权限资源(就是导航菜单),所以整个权限系统用起来好像也挺方便的,不过一旦权限资源多了起来目前的设计有点捉襟见肘了。假设我们有100个权限资源,A用户要设置50个权限,BCD三个用户也要设置这同样的50个权限,那么我必须为每个用户都重复操作50下才行!这种需求还特别特别常见,比如销售部门的员工都拥有同样的权限,每新来一个员工我就得给其一步一步重复地去设置权限,并且我要是更改这个销售部门的权限,那么旗下所有员工的权限都得一一更改,极其繁琐。
现在我们的权限关系是和用户绑定的,所以每有一个新用户我们就得为其设置一套专属的权限。既然很多用户的权限都是相同的,那么我再封装一层出来,屏蔽用户和权限之间的关系不就搞定了。
修改资源表,添加角色表、用户角色关联关系、角色资源关联关系

create table resource(id int primary key AUTO_INCREMENT comment '主键',path varchar(255) comment 'URL路径',name varchar(255) comment 'URL名称'
);insert into resource (path,name) value('/','首页');
insert into resource (path,name) value('/user/account','账户管理');
insert into resource (path,name) value('/user/role','角色管理');
insert into resource (path,name) value('/data','数据管理');create table role(id int primary key AUTO_INCREMENT comment '主键',name varchar(255) comment '角色名称'
);insert into role (name) value('超级管理员');
insert into role (name) value('数据管理员');create table role_resource(roleId int comment '角色id',resourceId int comment '资源id'
);-- 超级管理员
insert into role_resource (roleId,resourceId) value(1,1);
insert into role_resource (roleId,resourceId) value(1,2);
insert into role_resource (roleId,resourceId) value(1,3);
insert into role_resource (roleId,resourceId) value(1,4);
-- 数据管理员
insert into role_resource (roleId,resourceId) value(2,1);
insert into role_resource (roleId,resourceId) value(2,4);create table user_role(roleId int comment '角色id',userId int comment '用户id'
);
-- 用户1超级管理员
insert into user_role (roleId,userId) value(1,1);
-- 用户2
insert into user_role (roleId,userId) value(2,2);

实体

@Data
public class Resource {private int id;private String name;private String path;
}@Data
public class Role {private int id;private String name;
}@Data
public class RoleResource {private int roleId;private int resourceId;
}@Data
public class UserRole {private int roleId;private int userId;
}

修改ResouceMapper以及相关的service和controller

@Repository
@Mapper
public interface ResouceMapper extends BaseMapper<Resource> {/*** 获取用户的菜单* @param userName* @return*/List<Resource> getCurrentUserMenus(String userName);
}

修改ResouceMapper.xml

<?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.wujialiang.auth.mapper.ResouceMapper"><select id="getCurrentUserMenus" parameterType="java.lang.String" resultType="com.wujialiang.auth.entity.Resource">select t2.id,t2.name,t2.path from role_resource t1left join resource t2 on t2.id=t1.resourceIdwhere t1.roleId =(select t3.roleId from user_role t3 where t3.userId=(select t4.id from user t4 where t4.UserName=#{userName} limit 1)limit 1)</select>
</mapper>

用户1超级管理员权限如下
在这里插入图片描述

用户2数据管理员权限如下
在这里插入图片描述
后续请看下篇
springboot权限验证学习-下

参考

https://www.cnblogs.com/RudeCrab/p/14251274.html
https://www.cnblogs.com/RudeCrab/p/14251154.html
https://blog.csdn.net/qq1910506668/article/details/136608184


http://www.ppmy.cn/server/22815.html

相关文章

【随想录】Day32—第八章 贪心算法 part02

目录 题目1: 买卖股票的最佳时机 II1- 思路2- 题解⭐买卖股票的最佳时机 II ——题解思路 题目2: 55. 跳跃游戏1- 思路2- 题解⭐跳跃游戏 ——题解思路 题目3: 45. 跳跃游戏 II1- 思路2- 题解⭐跳跃游戏 II ——题解思路 题目1: 买卖股票的最佳时机 II 题目链接&#xff1a;12…

QT Windows 实现调用Windows API获取ARP 表

简介 使用ping方式获取网络可访问或者存在的设备发现部分会无法ping通但实际网络上存在此设备&#xff0c; 但使用arp -a却可以显示出来&#xff0c; 所以现在使用windows API的方式获取arp 表。 实现 参考Windows提供的示例转化成Qt Qt .pro LIBS -liphlpapiLIBS -lws2_32…

Rust之构建命令行程序(六):信息写入

开发环境 Windows 11Rust 1.77.2 VS Code 1.88.1 项目工程 这次创建了新的工程minigrep. 将错误信息写入标准错误而不是标准输出 此时&#xff0c;我们正在使用宏println!将所有输出写入终端。在大多数终端中&#xff0c;有两种输出:一般信息的标准输出&#xff08;stdout&…

在Visual Studio中查看C项目使用的C语言版本

在Visual Studio中查看C项目使用的C语言版本&#xff0c;可以通过以下步骤进行&#xff1a; 打开Visual Studio。 打开你的C项目。 右键点击项目名称&#xff0c;选择“属性”。 在弹出的属性页中&#xff0c;找到“配置属性” -> “C/C” -> “语言”。 在右侧的“…

数据结构初阶——树和二叉树

数据结构初阶——树和二叉树 1. 树的概念和结构1.1 树的概念1.2 树的表示 2. 二叉树2.1 二叉树的概念和结构2.2 二叉树的存储结构2.2.1 顺序存储2.2.2 链式存储 3. 二叉树的顺序结构及实现——堆3.1 堆的概念和结构3.2 堆的实现3.2.1 堆的定义3.2.2 堆的向上调整3.2.3 堆的向下…

go语言实现简单ngnix样例

目录 1、代码实现样例&#xff1a; 2、postman调用ngnix&#xff0c;转发&#xff1a; 1、代码实现样例&#xff1a; package mainimport ("bytes""encoding/json""io""log""net/http""net/http/httputil""…

共享单车(二):项目日志

stdin, stdout, stderr Linux系统下&#xff0c;当一个用户进程被创建时&#xff0c;与之对应的三个数据流&#xff08;stdin&#xff0c;stdout和stderr&#xff0c;即三个文件&#xff09;也会被创建。 stdin&#xff0c;标准输入文件&#xff0c;通常对应着终端的键盘。 s…

el-upload组件如何上传blob格式的url地址视频

el-upload组件如何上传blob格式的url地址视频 一、存在问题二、直接上代码 需求&#xff1a;想把视频地址url:“blob:http://localhost:8083/65bd3c0f-52ec-4844-b85e-06fdb5095b7b”&#xff0c;通过el-upload组件上传 el-upload是Element UI中用于文件上传的组件&#xff0c;…

论文解读:(UPL)Unsupervised Prompt Learning for Vision-Language Models

文章汇总 存在的问题 之前的来自目标数据集的标记数据(有监督学习)可能会限制可伸缩性。 动机 通过无监督提示学习(UPL)方法&#xff0c;以避免提示工程&#xff0c;同时提高类clip视觉语言模型的迁移性能。 方法解读 主张top-k而不是top-p 注&#xff1a;top-k是指挑选…

springboot整合https使用自签名证书实现浏览器和服务器之间的双向认证

效果描述&#xff1a;本地环境 两台以上电脑 可以实现安装客户端证书的电脑可以访问springboot启动项目&#xff0c;没有安装客户端证书的电脑无法访问springboot启动项目 1.操作&#xff1a;需要安装openssl工具 工具包&#xff1a;Win64OpenSSL_Light-3_3_0.exe 或者Win6…

TablePlus for Mac/Win:开启高效数据开发新纪元

在当今数字化时代&#xff0c;数据的重要性日益凸显。无论是企业还是个人&#xff0c;都需要一款强大而实用的本地原生数据开发软件来提升工作效率。而 TablePlus for Mac/Win 正是这样一款卓越的工具&#xff0c;它为用户带来了全新的体验&#xff0c;让数据开发变得更加轻松、…

CP2532电容触摸检测芯片

这份文件是Chiphomer Technology (Shanghai) Limited提供的CP2532电容触摸检测芯片的用户手册&#xff0c;版本R2.0&#xff0c;发布日期为2008年10月21日。以下是该文件的核心内容概要&#xff1a; 产品简介 CP2532 是一款支持12通道的电容式触摸传感芯片。内嵌高精度电容数…

Stable Diffusion教程:文生图

最近几天AI绘画没有什么大动作&#xff0c;正好有时间总结下Stable Diffusion的一些基础知识&#xff0c;今天就给大家再唠叨一下文生图这个功能&#xff0c;会详细说明其中的各个参数。 文生图是Stable Diffusion的核心功能&#xff0c;它的核心能力就是根据提示词生成相应的…

CSS border边框(理解网页边框制作)

目录 一、border边框介绍 1.概念 2.特点 3.功能 4.应用 二、border边框用法 1.border边框属性 2.边框样式 3.边框宽度 4.边框颜色 5.边框-单独设置各边 6.边框-简写属性 三、border边框属性 四、border边框实例 1.创建带有阴影效果的边框&#xff1a; 2. 创建一个类似标…

C++深度解析教程笔记2

C深度解析教程笔记2 第3课 - 进化后的 const 分析实验-C与C的const区别实验-C与C的const区别&const作用域 第4课 - 布尔类型和引用小结 本文学习自狄泰软件学院 唐佐林老师的 C深度解析教程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 第3课 - 进化后…

ElasticSearch教程入门到精通——第五部分(基于ELK技术栈elasticsearch 7.x+8.x新特性)

ElasticSearch教程入门到精通——第五部分&#xff08;基于ELK技术栈elasticsearch 7.x8.x新特性&#xff09; 1. Elasticsearch集成1.1 框架集成-SpringData-整体介绍1.2 Spring Data Elasticsearch 介绍1.3 框架集成-SpringData-代码功能集成1.3.1 创建Maven项目1.3.2 修改po…

如何在Flask应用程序中使用JSON Web Tokens进行安全认证

密码、信用卡信息、个人识别号码&#xff08;PIN&#xff09;——这些都是用于授权和认证的关键资产。这意味着它们需要受到未经授权的用户的保护。 作为开发者&#xff0c;我们的任务是保护这些敏感信息&#xff0c;并且在我们的应用程序中实施强大的安全措施非常重要。 现在…

振弦采集仪在岩土工程监测中的应用案例与成果展示

振弦采集仪在岩土工程监测中的应用案例与成果展示 河北稳控科技振弦采集仪是一种岩土工程监测仪器&#xff0c;通过采集地下振动信号来获取地下构造的动态特性&#xff0c;具有高精度、高灵敏度和高实时性的特点。在岩土工程监测中&#xff0c;振弦采集仪广泛应用于地基沉降、…

Matlab实现CNN-LSTM模型,对一维时序信号进行分类

1、利用Matlab2021b训练CNN-LSTM模型&#xff0c;对采集的一维时序信号进行分类二分类或多分类 2、CNN-LSTM时序信号多分类执行结果截图 训练进度&#xff1a; 网络分析&#xff1a; 指标变化趋势&#xff1a; 代码下载方式&#xff08;代码含数据集与模型构建&#xff0c;附…

【python技术】使用akshare抓取东方财富所有概念板块,并把指定板块概念的成分股保存excel 简单示例

最近有个想法&#xff0c;分析A股某个概念成分股情况进行分析&#xff0c;第一反应是把对应概念板块的成分股爬取下来。说干就干 下面是简单示例 import akshare as ak import pandas as pddef fetch_and_save_concept_stocks(name):# 获取指定股票概念的成分股&#xff0c;并…