从0开始搭建一个生产级SpringBoot2.0.X项目(十二)SpringBoot接口SpringSecurity JWT鉴权

前言

最近有个想法想整理一个内容比较完整springboot项目初始化Demo。

SpringBoot接口权限控制 SpringSecurity 接口使用 Bearer token类型  JWT 鉴权

一、pom文件新增依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.12.6</version>
</dependency>

application.yaml 增加参数 用于模拟后续生成TOKEN和验证使用

#JTW 加密的密码
jwt:# secret 必须足够长 40bits之上# 否则 报错 The specified key byte array is 40 bits which is not secure enough            #for any JWT HMAC-SHA algorithmsecret: 12345678123456781234567812345678
#认证系统编号 clientid: clientid

二、创建 UserInfo 类 模拟用户信息

import lombok.Data;/*** 用户信息*/
@Data
public class UserInfo {private String clientid;//用户授权idprivate String username;//用户名private String password;//用户名
}

三、创建 JwtTokenUtil 类,用于生成和验证JWT令牌。

package com.murg.bootdemo.util;import com.murg.bootdemo.business.entity.UserInfo;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SecureDigestAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import javax.crypto.SecretKey;
import java.io.Serializable;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;@Component
public class JwtTokenUtil implements Serializable {/*** 过期时间(单位:秒)*/public static final Integer REFRESH_TOKEN_EXPIRE = 72000;/*** 加密算法*/private final static SecureDigestAlgorithm<SecretKey, SecretKey> ALGORITHM = Jwts.SIG.HS256;/*** 私钥*/private static String SECRET;/*** 秘钥实例*/public static SecretKey KEY;/*** jwt签发者*/private final static String JWT_ISS = "Sunreal";/*** jwt主体*/private final static String SUBJECT = "dwd";@Value("${jwt.secret}")public void setSecret(String secret) {JwtTokenUtil.SECRET = secret;JwtTokenUtil.KEY=Keys.hmacShaKeyFor(SECRET.getBytes());}/*** 获取令牌用户数据* @param token* @return*/public static UserInfo getUserInfoByToken(String token) {final Claims claims = parsePayload(token);UserInfo userInfo = null;if (null != claims) {userInfo = new UserInfo();userInfo.setClientid(claims.get("clientid").toString());userInfo.setUsername(claims.get("username").toString());}return userInfo;}/*** 获取令牌用户数据* @param token* @return*/public static String getUsernameFromToken(String token) {final Claims claims = parsePayload(token);UserInfo userInfo = null;if (null != claims) {userInfo = new UserInfo();userInfo.setClientid(claims.get("clientid").toString());userInfo.setUsername(claims.get("username").toString());}return userInfo.getUsername();}/*** 令牌过期校验 true-过期 false-未过期* @param token* @return*/public static Boolean isExpired(String token) {Boolean result = true;final Claims claims = parsePayload(token);if (null != claims) {String exp = claims.get("exp").toString();long diff = Long.parseLong(exp) - System.currentTimeMillis() / 1000;if(diff > 0) {result = false;}}return result;}public Boolean validateToken(String token, UserDetails userDetails) {final String username = getUsernameFromToken(token);return (username.equals(userDetails.getUsername()) && !isExpired(token));}/*** 获取令牌的过期时间*/public static Long getTokenExpireIn(String token) {Long expireIn = System.currentTimeMillis() / 1000;final Claims claims = parsePayload(token);if (null != claims) {String exp = claims.get("exp").toString();return Long.parseLong(exp);}return expireIn;}public String generateToken(UserDetails userDetails,String clientid) {Map<String, Object> claims = new HashMap<>();claims.put("username",userDetails.getUsername());// claims.put("password",userDetails.getPassword());claims.put("clientid",clientid);return generateToken(claims);}/*** 生成令牌*/public static String generateToken(Map<String, Object> payload) {Date expireDate = Date.from(Instant.now().plusSeconds(REFRESH_TOKEN_EXPIRE));return Jwts.builder().header().add("typ", "JWT").add("alg", "HS256").and().claims(payload).id(UUID.randomUUID().toString()).expiration(expireDate).issuedAt(new Date()).subject(SUBJECT).issuer(JWT_ISS).signWith(KEY, ALGORITHM).compact();}/*** 解析令牌claims*/public static Jws<Claims> parseClaim(String token) {Jws<Claims> claimsJws = null;claimsJws = Jwts.parser() .verifyWith(KEY) .build().parseSignedClaims(token);return claimsJws;}/*** 解析令牌header*/private static JwsHeader parseHeader(String token) {Jws<Claims> claimsJws = parseClaim(token);if(null == claimsJws) {return null;}return claimsJws.getHeader();}/*** 解析令牌payload*/private static Claims parsePayload(String token) {Jws<Claims> claimsJws = parseClaim(token);if(null == claimsJws) {return null;}return claimsJws.getPayload();}
}

四、创建 JwtAuthenticationEntryPoint 类,用于处理未经授权的请求

package com.murg.bootdemo.filter;import com.murg.bootdemo.common.WebResult;
import com.murg.bootdemo.util.JsonUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;/**此类继承Spring Security的AuthenticationEntryPoint类,并重写其commence。它拒绝每个未经身份验证的请求并发送错误代码401。*/
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {private static final long serialVersionUID = -7858869558953243875L;@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response,AuthenticationException authException) throws IOException, ServletException {//返回统一封装的格式WebResult<Object> webResult = WebResult.ok();webResult.isWrong("您可能未登录或登录超时,请重新登陆!");response.setContentType("application/json");response.setStatus(401);try {JsonUtil.write2Stream(response.getOutputStream(), webResult);} catch (Exception var6) {throw new ServletException();}}
}

五、创建JWTUserDetailsService以便使用用户名从数据库中获取用户详细信息。

package com.murg.bootdemo.interceptor;import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;import java.util.ArrayList;/*** JWTUserDetailsService* JWTUserDetailsService实现了Spring Security UserDetailsService接口。* 它会覆盖loadUserByUsername,以便使用用户名从数据库中获取用户详细信息。* 当对用户提供的用户详细信息进行身份验证时,Spring Security Authentication Manager调用此方法从数据库中获取用户详细信息* 在这里,我们从硬编码的用户列表中获取用户详细信息。* 以后实际会从数据库中获取用户详细信息的DAO实现。* 用户密码也使用BCrypt以加密格式存储。在这里,您可以使用在线Bcrypt生成器为密码生成Bcrypt。*/
@Service
public class JwtUserDetailsService implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {if ("admin".equals(username)) {return new User("admin", "$2a$10$XRemBdMT1hrf4EdfsDWHCeSQFczKq0STVDqVRXT8YnbPoQcN21KAi",new ArrayList<>());} else {throw new UsernameNotFoundException("User not found with username: " + username);}}/*** 测试生成BCrypt加密格式* @param args*/public static void main(String[] args) {String pass = "123456";BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();final String passHash = encoder.encode(pass);System.out.println(passHash);final boolean matches = encoder.matches(pass, passHash);System.out.println(matches);}
}

六、创建 JwtRequestFilter 类,用于解析和验证JWT 

package com.murg.bootdemo.filter;import com.murg.bootdemo.interceptor.JwtUserDetailsService;
import com.murg.bootdemo.util.JwtTokenUtil;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/**JwtRequestFilter继承了Spring Web的OncePerRequestFilter类。对于任何传入请求,都会执行此Filter类。它检查请求是否具有有效的JWT令牌。如果它具有有效的JWT令牌,则它将在上下文中设置Authentication,以指定当前用户已通过身份验证。*/
@Slf4j
@Component
public class JwtRequestFilter extends OncePerRequestFilter {private  final JwtUserDetailsService jwtUserDetailsService;private final JwtTokenUtil jwtTokenUtil;public JwtRequestFilter(JwtUserDetailsService jwtUserDetailsService, JwtTokenUtil jwtTokenUtil) {this.jwtTokenUtil = jwtTokenUtil;this.jwtUserDetailsService = jwtUserDetailsService;}@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {final String requestTokenHeader = request.getHeader("Authorization");String username = null;String jwtToken = null;// JWT Token is in the form "Bearer token". Remove Bearer word and get only the Tokenif (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {jwtToken = requestTokenHeader.substring(7);try {username = jwtTokenUtil.getUserInfoByToken(jwtToken).getUsername();} catch (IllegalArgumentException e) {log.error("Unable to get JWT Token");} catch (ExpiredJwtException e) {log.error("JWT Token has expired"); //token 过期抛出异常 用于统一异常处理ExceptionConfig捕获  throw e;}} else {logger.warn("JWT Token does not begin with Bearer String");}//Once we get the token validate it.if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {//根据用户名获取库里的用户信息UserDetails userDetails = this.jwtUserDetailsService.loadUserByUsername(username);// if token is valid configure Spring Security to manually set authenticationif (jwtTokenUtil.validateToken(jwtToken, userDetails)) { //校验用户信息 用户名一致并且token没过期/* 创建了一个UsernamePasswordAuthenticationToken实例,并通过调用setDetails方法设置了额外的细节。这个细节可以是任何对象,通常用于传递额外的信息,如客户端IP地址、会话ID等。请注意,setDetails方法不是为了设置用户名和密码,而是为了设置与认证过程相关的其他信息。如果你需要设置用户名和密码,应该在创建UsernamePasswordAuthenticationToken实例时提供。*/UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));// 设置SecurityContext中的Authentication对象 在上下文中设置Authentication,以指定当前用户已通过身份验证SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);}}chain.doFilter(request, response);}
}

七、增加配置Spring Security类 WebSecurityConfig

package com.murg.bootdemo.config;import com.murg.bootdemo.filter.JwtAuthenticationEntryPoint;
import com.murg.bootdemo.filter.JwtRequestFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;private final UserDetailsService jwtUserDetailsService;private final JwtRequestFilter jwtRequestFilter;public WebSecurityConfig(JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint,UserDetailsService jwtUserDetailsService,JwtRequestFilter jwtRequestFilter) {this.jwtAuthenticationEntryPoint = jwtAuthenticationEntryPoint;this.jwtUserDetailsService = jwtUserDetailsService;this.jwtRequestFilter = jwtRequestFilter;}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/***认证管理器的建造器,如果要使用该功能需要配置一个userDetailsService和PasswordEncoder;为什么要配置UserDetailService,他的作用是什么?因为我们需要被认证的用户作为管理,那么UserDetailService作用就是:在认证器中根据传过来的用户名进行查找,PasswordEncoder就是将密码进行比对;认证成功后返回一个Authentication对象自定义用户认证的service 用于实现自己的认证逻辑passwordEncoder定义密码解密方式**/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception{auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());}@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {// We don't need CSRF for this examplehttpSecurity.csrf().disable()// 不验证此特定请求 该请求为获取token的接口.authorizeRequests().antMatchers("/authenticate").permitAll()// 所有其他请求都需要经过身份验证.anyRequest().authenticated().and()//Spring Security中用于配置认证入口点异常处理的一部分。当Spring Security遇到认证异常时,// 它会通过配置的AuthenticationEntryPoint来处理。.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()// 所有的Rest服务一定要设置为无状态,以提升操作性能.sessionCreationPolicy(SessionCreationPolicy.STATELESS);// Add a filter to validate the tokens with every requesthttpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);}}

八、测试接口鉴权

重启服务不带token测试helloWord接口,返回鉴权失败

创建GetJwtController

调用上述预留放开的/authenticate接口获取token数据

package com.murg.bootdemo.business.controller;import com.murg.bootdemo.common.WebResult;
import com.murg.bootdemo.util.JwtTokenUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;import java.util.Objects;/**使用JwtAuthenticationController暴露/authenticate。使用Spring Authentication Manager验证用户名和密码。如果凭据有效,则会使用JWTTokenUtil创建一个JWT令牌并将其提供给客户端。*/
@RestController
public class GetJwtController {@Value("${jwt.clientid}")private String CLIENTID;private final AuthenticationManager authenticationManager;private final JwtTokenUtil jwtTokenUtil;private final UserDetailsService jwtInMemoryUserDetailsService;public GetJwtController(AuthenticationManager authenticationManager,JwtTokenUtil jwtTokenUtil,UserDetailsService jwtInMemoryUserDetailsService) {this.authenticationManager = authenticationManager;this.jwtTokenUtil = jwtTokenUtil;this.jwtInMemoryUserDetailsService = jwtInMemoryUserDetailsService;}@RequestMapping(value = "/authenticate", method = RequestMethod.POST)public WebResult createAuthenticationToken(@RequestBody JwtRequest authenticationRequest)throws Exception {if(!CLIENTID.equals(authenticationRequest.getClientid())) {throw new Exception( "客户端未授权");}//   authenticate(authenticationRequest.getUsername(), authenticationRequest.getPassword());final UserDetails userDetails = jwtInMemoryUserDetailsService.loadUserByUsername(authenticationRequest.getUsername());final String token = jwtTokenUtil.generateToken(userDetails,authenticationRequest.getClientid());return WebResult.ok(token);}/***  这个方法会调用UserDetailsServiceImpl.loadUserByUsername,此过程中调用的流程如下:* 1. AuthenticationManager是个接口,ProviderManager是他的实现类。* 2. authenticationManager.authenticate()调用其实就是ProviderManager.authenticate()* 3. ProviderManager代码中找到provider.authenticate(authentication);* 4. provider.authenticate(authentication)是AuthenticationProvider.authenticate(authentication)方法。* 5. AuthenticationProvider也是一个接口,他的实现类是AbstractUserDetailsAuthenticationProvider。* 6. AbstractUserDetailsAuthenticationProvider的子类是DaoAuthenticationProvider。* 7. 在DaoAuthenticationProvider中调用了UserDetailsService8.写一个UserDetailsServiceImpl实现类JwtUserDetailsService ,实现UserDetailsService ,并重写loadUserByUsername()直接调用UserDetails userDetails = jwtInMemoryUserDetailsService.loadUserByUsername(authenticationRequest.getUsername());*/private void authenticate(String username, String password) throws Exception {Objects.requireNonNull(username);Objects.requireNonNull(password);try {authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));} catch (DisabledException e) {throw new Exception("USER_DISABLED", e);} catch (BadCredentialsException e) {throw new Exception("INVALID_CREDENTIALS", e);}}
}

 

拿到token之后 重新带 Bearer 类型 token 测试 helloWord接口 正常返回

九、配置鉴权白名单

通过application.yaml配置放行鉴权路由白名单

1.增加配置 放行getTt26接口。

oauth:whitelist:- /getTt26

2.创建配置类 AuthwhitelistConfig 定义属性 whitelist 

参数配置在 application.yaml 文件中,通过 @ConfigurationProperties 注解注入参数whitelist

package com.murg.bootdemo.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;import java.util.List;
@Data
@Configuration
@ConfigurationProperties("oauth")
public class AuthwhitelistConfig {private List<String> whitelist;
}

3修改 WebSecurityConfig方法

引入AuthwhitelistConfig 

private final AuthwhitelistConfig authwhitelist;

 增加白名单不验证

 .antMatchers(authwhitelist.getWhitelist().toArray(new String[0])).permitAll()

@Overrideprotected void configure(HttpSecurity httpSecurity) throws Exception {// We don't need CSRF for this examplehttpSecurity.csrf().disable()// 不验证此特定请求.authorizeRequests().antMatchers("/authenticate").permitAll().antMatchers(authwhitelist.getWhitelist().toArray(new String[0])).permitAll()// 所有其他请求都需要经过身份验证.anyRequest().authenticated().and()//Spring Security中用于配置认证入口点异常处理的一部分。当Spring Security遇到认证异常时,// 它会通过配置的AuthenticationEntryPoint来处理。.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()// 所有的Rest服务一定要设置为无状态,以提升操作性能.sessionCreationPolicy(SessionCreationPolicy.STATELESS);// Add a filter to validate the tokens with every requesthttpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);}

不带token测试接口

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/60310.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaEE初阶---properties类+反射+注解

文章目录 1.配置文件properities2.快速上手3.常见方法3.1读取配置文件3.2获取k-v值3.3修改k-v值3.4unicode的说明 4.反射的引入4.1传统写法4.2反射的写法&#xff08;初识&#xff09;4.3反射的介绍4.4获得class类的方法4.5所有类型的class对象4.6类加载过程4.7类初始化的过程4…

【React】深入理解 JSX语法

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 深入理解 JSX语法1. JSX 简介2. JSX 的基本语法2.1 基本结构2.2 与普通 JavaScr…

Spark中给读取到的数据 的列 重命名的几种方式!

目录 一、第一种 (withColumnRenamed) 二、第二种&#xff08;toDF&#xff09; 三、第三种&#xff08; toDF(*tuple1) &#xff09; 四、 第四种(schema) 五、假如文件里自带有列名的情况&#xff08;option&#xff09; 一、第一种 (withColumnRenamed) 假设要把如下…

M1M2 MAC安装windows11 虚拟机的全过程

M1/M2 MAC安装windows11 虚拟机的全过程 这两天折腾了一下windows11 arm架构的虚拟机&#xff0c;将途中遇到的坑总结一下。 1、虚拟机软件&#xff1a;vmware fusion 13.6 或者 parallel 19 &#xff1f; 结论是&#xff1a;用parellel 19。 这两个软件都安装过&#xff0…

IEEE JSSC更新|Tiny Tapeout:让每个人都能设计定制芯片

简介 由于成本高昂且需要专业技术&#xff0c;设计和制造定制集成电路的传统上仅限于大型公司和机构。然而&#xff0c;名为Tiny Tapeout的创新项目正在改变这一现状&#xff0c;让业余爱好者、学生和小型团队也能设计定制芯片。本文将探讨Tiny Tapeout的工作原理&#xff0c;以…

Java:一段代码,无限可能

Java&#xff0c;诞生于1995年&#xff0c;如今已走过近三十载春秋。它历经互联网泡沫的兴衰、移动互联网的浪潮&#xff0c;以及云计算和大数据的洗礼&#xff0c;依然屹立在编程语言的舞台中央&#xff0c;散发着耀眼的光芒。这篇文章将带你回顾Java的辉煌历史&#xff0c;探…

《XGBoost算法的原理推导》12-14决策树复杂度的正则化项 公式解析

本文是将文章《XGBoost算法的原理推导》中的公式单独拿出来做一个详细的解析&#xff0c;便于初学者更好的理解。 我们定义一颗树的复杂度 Ω Ω Ω&#xff0c;它由两部分组成&#xff1a; 叶子结点的数量&#xff1b;叶子结点权重向量的 L 2 L2 L2范数&#xff1b; 公式(…

Linux【基础篇】

-- 原生罪 linux的入门安装学习 什么是操作系统&#xff1f; 用户通过操作系统和计算机硬件联系使用。桥梁~ 什么是Linux&#xff1f; 他是一套开放源代码&#xff08;在互联网上找到Linux系统的源代码&#xff0c;C语言写出的软件&#xff09;&#xff0c;可以自由 传播&…

大数据技术在智慧医疗中的应用

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 大数据技术在智慧医疗中的应用 大数据技术在智慧医疗中的应用 大数据技术在智慧医疗中的应用 引言 大数据技术概述 定义与原理 发…

Linux(CentOS)安装 MySQL

CentOS版本&#xff1a;CentOS 7 三种安装方式&#xff1a; 一、通过 yum 安装&#xff0c;最简单&#xff0c;一键安装&#xff0c;全程无忧。 二、通过 rpm 包安装&#xff0c;需具备基础概念及常规操作。 三、通过 gz 包安装&#xff0c;需具备配置相关操作。 --------…

CSS如何改变滚动条的颜色样式粗细?

默认滚动条很丑怎么办&#xff1f;如何改版滚动条的粗细&#xff0c;颜色&#xff0c;让它更美观&#xff1f;CSS如何改变滚动条的粗细&#xff1f; 干货来了 /* Webkit内核浏览器的滚动条样式 */ ::-webkit-scrollbar {width: 4px; /* 设置滚动条的宽度 */ }::-webkit-scroll…

YOLOv11(Ultralytics)可视化界面ui设计,基于pyqt5,单文件即插即用,支持文件夹检测及云摄像头检测并保存

本文的可视化界面对于YOLOv11/Ultralytics/YOLOv8的检测、分割、分类、姿势估算&#xff08;detection, segmentation, obb, classification, and pose estimation&#xff09;等均可正常显示。本次新增了图片及视频的保存&#xff0c;可以选择传入文件夹进行检测并显示&#x…

用python开发坦克大战重制版

Python 开发坦克大战重制版&#xff1a;全面教程 引言 坦克大战是一款经典的街机游戏&#xff0c;自1985年首次推出以来&#xff0c;便吸引了无数玩家。随着时间的推移&#xff0c;许多游戏开发者开始尝试重制这款经典游戏。本文将指导你如何使用 Python 和 Pygame 库开发一个…

链式结构二叉树

数据结构 结点申请 树&#x1f332;行结构 前中后序遍历 二叉树结点个数 二叉树叶子节点个数 第k层结点个数 二叉树深度 二叉树查找值为x的结点 二叉树销毁

【2024软考架构案例题】你知道什么是 RESTful 风格吗?

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…

#渗透测试#SRC漏洞挖掘#深入挖掘CSRF漏洞02

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…

自动驾驶革命:从特斯拉到百度,谁将主宰未来交通?

内容概要 自动驾驶技术正在经历一个前所未有的革命性变化&#xff0c;各大企业纷纷抢占这一充满潜力的新市场。以特斯拉和百度为代表的行业巨头&#xff0c;正利用各自的优势在这一技术的赛道上展开激烈竞争。特斯拉凭借其在电动汽车和自动驾驶领域的前瞻性设计与不断革新的技…

反向代理开发

1 概念 1.1 反向代理概念 反向代理是指以代理服务器来接收客户端的请求&#xff0c;然后将请求转发给内部网络上的服务器&#xff0c;将从服务器上得到的结果返回给客户端&#xff0c;此时代理服务器对外表现为一个反向代理服务器。 对于客户端来说&#xff0c;反向代理就相当于…

简记Vue3(五)—— Pinia

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

C++11语法介绍(1) -- 列表初始化{},左值和右值,右值引用和移动语义,引用折叠,完美转发

目录 1.C11的发展时间线 2.列表初始化 2.1C98传统的{} 2.2C11中的{} 2.3C11中的std::initializer_list 2.3.1vector中initializer list构造的模拟实现 2.3.2以vector为例演示initializer_list版本的构造 3.右值引用和移动语义 3.1左值和右值 3.2左值引用和右值引用 3.3…