微服务OAuth 2.1认证授权Demo方案(Spring Security 6)

文章目录

  • 一、介绍
  • 二、auth微服务代码
      • 1. SecurityConfig
      • 2. UserDetailsService
      • 3. 总结
  • 三、gateway微服务代码
      • 1. 统一处理CORS问题
  • 四、content微服务代码
      • 1. controller
      • 2. SecurityConfig
      • 3. 解析JWT Utils
      • 4. 总结
  • 五、一些坑

书接上文
微服务OAuth 2.1认证授权可行性方案(Spring Security 6)

一、介绍

三个微服务

  • auth微服务作为认证服务器,用于颁发JWT
  • gateway微服务作为网关,用于拦截过滤。
  • content微服务作为资源服务器,用于校验授权。

以下是授权相关数据库。

  • user表示用户表
  • role表示角色表
  • user_role关联了用户和角色,表示某个用户是是什么角色。一个用户可以有多个角色
  • menu表示资源权限表。@PreAuthorize("hasAuthority('xxx')")时用的就是这里的code
  • permission关联了角色和资源权限,表示某个角色用于哪些资源访问权限,一个角色有多个资源访问权限。
    在这里插入图片描述

当我们知道userId,我们就可以知道这个用户可以访问哪些资源,并把这些权限(也就是menu里的code字段)写成数组,写到JWT的负载部分的authorities字段中。当用户携带此JWT访问具有@PreAuthorize("hasAuthority('xxx')")修饰的资源时,我们解析出JWT中的authorities字段,判断是否包含hasAuthority指定的xxx权限,以此来完成所谓的的“授权”。

二、auth微服务代码

1. SecurityConfig

package com.xuecheng.auth.config;import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
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.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.time.Duration;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;/*** 身份验证服务器安全配置** @author mumu* @date 2024/02/13*/
//@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@Configuration
@EnableWebSecurity
public class AuthServerSecurityConfig {private static KeyPair generateRsaKey() {KeyPair keyPair;try {KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");keyPairGenerator.initialize(2048);keyPair = keyPairGenerator.generateKeyPair();} catch (Exception ex) {throw new IllegalStateException(ex);}return keyPair;}/*** 密码编码器* 用于加密认证服务器client密码和用户密码** @return {@link PasswordEncoder}*/@Beanpublic PasswordEncoder passwordEncoder() {// 密码为明文方式// return NoOpPasswordEncoder.getInstance();// 或使用 BCryptPasswordEncoderreturn new BCryptPasswordEncoder();}/*** 授权服务器安全筛选器链* <br/>* 来自Spring Authorization Server示例,用于暴露Oauth2.1端点,一般不影响常规的请求** @param http http* @return {@link SecurityFilterChain}* @throws Exception 例外*/@Bean@Order(1)public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)throws Exception {OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults());    // Enable OpenID Connect 1.0http// Redirect to the login page when not authenticated from the// authorization endpoint.exceptionHandling((exceptions) -> exceptions.defaultAuthenticationEntryPointFor(new LoginUrlAuthenticationEntryPoint("/login"),new MediaTypeRequestMatcher(MediaType.TEXT_HTML)))// Accept access tokens for User Info and/or Client Registration.oauth2ResourceServer((resourceServer) -> resourceServer.jwt(Customizer.withDefaults()));return http.build();}/*** 默认筛选器链* <br/>* 这个才是我们需要关心的过滤链,可以指定哪些请求被放行,哪些请求需要JWT验证** @param http http* @return {@link SecurityFilterChain}* @throws Exception 例外*/@Bean@Order(2)public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) ->authorize.requestMatchers(new AntPathRequestMatcher("/actuator/**")).permitAll().requestMatchers(new AntPathRequestMatcher("/login")).permitAll().requestMatchers(new AntPathRequestMatcher("/logout")).permitAll().requestMatchers(new AntPathRequestMatcher("/wxLogin")).permitAll().requestMatchers(new AntPathRequestMatcher("/register")).permitAll().requestMatchers(new AntPathRequestMatcher("/oauth2/**")).permitAll().requestMatchers(new AntPathRequestMatcher("/**/*.html")).permitAll().requestMatchers(new AntPathRequestMatcher("/**/*.json")).permitAll().requestMatchers(new AntPathRequestMatcher("/auth/**")).permitAll().anyRequest().authenticated()).csrf(AbstractHttpConfigurer::disable)//指定logout端点,用于退出登陆,不然二次获取授权码时会自动登陆导致短时间内无法切换用户.logout(logout -> logout.logoutUrl("/logout").addLogoutHandler(new SecurityContextLogoutHandler()).logoutSuccessUrl("http://www.51xuecheng.cn")).formLogin(Customizer.withDefaults()).oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())
//                .jwt(jwt -> jwt
//                        .jwtAuthenticationConverter(jwtAuthenticationConverter())
//                ));return http.build();}private JwtAuthenticationConverter jwtAuthenticationConverter() {JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();return jwtConverter;}/*** 客户端管理实例* <br/>* 来自Spring Authorization Server示例** @return {@link RegisteredClientRepository}*/@Beanpublic RegisteredClientRepository registeredClientRepository() {RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString()).clientId("XcWebApp").clientSecret(passwordEncoder().encode("XcWebApp")).clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).redirectUri("http://www.51xuecheng.cn").redirectUri("http://localhost:63070/auth/wxLogin").redirectUri("http://www.51xuecheng.cn/sign.html")
//                .postLogoutRedirectUri("http://localhost:63070/login?logout").scope("all").scope(OidcScopes.OPENID).scope(OidcScopes.PROFILE).scope("message.read").scope("message.write").scope("read").scope("write").clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).tokenSettings(TokenSettings.builder().accessTokenTimeToLive(Duration.ofHours(2))  // 设置访问令牌的有效期.refreshTokenTimeToLive(Duration.ofDays(3))  // 设置刷新令牌的有效期.reuseRefreshTokens(true)                   // 是否重用刷新令牌.build()).build();return new InMemoryRegisteredClientRepository(registeredClient);}/*** jwk源* <br/>* 对访问令牌进行签名的示例,里面包含公私钥信息。** @return {@link JWKSource}<{@link SecurityContext}>*/@Beanpublic JWKSource<SecurityContext> jwkSource() {KeyPair keyPair = generateRsaKey();RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();RSAKey rsaKey = new RSAKey.Builder(publicKey).privateKey(privateKey).keyID(UUID.randomUUID().toString()).build();JWKSet jwkSet = new JWKSet(rsaKey);return new ImmutableJWKSet<>(jwkSet);}/*** jwt解码器* <br/>* JWT解码器,主要就是基于公钥信息来解码** @param jwkSource jwk源* @return {@link JwtDecoder}*/@Beanpublic JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);}@Beanpublic AuthorizationServerSettings authorizationServerSettings() {return AuthorizationServerSettings.builder().build();}/*** JWT定制器* <BR/>* 可以往JWT从加入额外信息,这里是加入authorities字段,是一个权限数组。** @return {@link OAuth2TokenCustomizer}<{@link JwtEncodingContext}>*/@Beanpublic OAuth2TokenCustomizer<JwtEncodingContext> jwtTokenCustomizer() {return context -> {Authentication authentication = context.getPrincipal();if (authentication.getPrincipal() instanceof UserDetails userDetails) {List<String> authorities = userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());context.getClaims().claim("authorities", authorities);}};}
}

这里需要注意几点

  • 使用BCryptPasswordEncoder密码加密,在设置clientSecret时需要手动使用密码编码器。
  • jwtTokenCustomizer解析UserDetails然后往JWT中添加authorities字段,为了后面的授权。

2. UserDetailsService

package com.xuecheng.ucenter.service.impl;import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.ucenter.mapper.XcMenuMapper;
import com.xuecheng.ucenter.mapper.XcUserMapper;
import com.xuecheng.ucenter.model.dto.AuthParamsDto;
import com.xuecheng.ucenter.model.dto.XcUserExt;
import com.xuecheng.ucenter.model.po.XcMenu;
import com.xuecheng.ucenter.model.po.XcUser;
import com.xuecheng.ucenter.service.AuthService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
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.stereotype.Component;import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;@Component
@Slf4j
public class UserServiceImpl implements UserDetailsService {@Autowiredprivate MyAuthService myAuthService;@AutowiredXcMenuMapper xcMenuMapper;/*** 用户统一认证** @param s 用户信息Json字符串* @return {@link UserDetails}* @throws UsernameNotFoundException 找不到用户名异常*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {XcUserExt xcUserExt = myAuthService.execute(username);return getUserPrincipal(xcUserExt);}public UserDetails getUserPrincipal(XcUserExt user){//用户权限,如果不加报Cannot pass a null GrantedAuthority collectionList<XcMenu> xcMenus = xcMenuMapper.selectPermissionByUserId(user.getId());String[] permissions = {"read"};if (ObjectUtils.isNotEmpty(xcMenus)){permissions = xcMenus.stream().map(XcMenu::getCode).toList().toArray(String[]::new);log.info("权限如下:{}", Arrays.toString(permissions));}//为了安全在令牌中不放密码String password = user.getPassword();user.setPassword(null);//将user对象转jsonString userString = JSON.toJSONString(user);//创建UserDetails对象return User.withUsername(userString).password(password).authorities(permissions).build();}
}

这里需要注意几点

  • username就是前端/auth/login的时候输入的账户名。
  • myAuthService.execute(username)不抛异常,就默认表示账户存在,此时将password加入UserDetails 并返回,Spring Authorization Server对比校验两个密码。
  • myAuthService.execute(username)根据username获取用户信息返回,将用户信息存入withUsername中,Spring Authorization Server默认会将其加入到JWT中。
  • 现在Spring Authorization Server默认不会把authorities(permissions)写入JWT,需要配合OAuth2TokenCustomizer手动写入。

3. 总结

这样,auth微服务颁发的JWT,现在就会包含authorities字段。示例如下

{"active": true,"sub": "{\"cellphone\":\"17266666637\",\"createTime\":\"2024-02-13 10:33:13\",\"email\":\"1138882663@qq.com\",\"id\":\"012f3a90-2bc9-4a2c-82a3-f9777c9ac10a\",\"name\":\"xiamu\",\"nickname\":\"xiamu\",\"permissions\":[],\"status\":\"1\",\"updateTime\":\"2024-02-13 10:33:13\",\"username\":\"xiamu\",\"utype\":\"101001\",\"wxUnionid\":\"test\"}","aud": ["XcWebApp"],"nbf": 1707830437,"scope": "all","iss": "http://localhost:63070/auth","exp": 1707837637,"iat": 1707830437,"jti": "8a657c60-968f-4d98-8a4c-22a7b4ecd333","authorities": ["xc_sysmanager","xc_sysmanager_company","xc_sysmanager_doc","xc_sysmanager_log","xc_teachmanager_course_list"],"client_id": "XcWebApp","token_type": "Bearer"
}

三、gateway微服务代码

1. 统一处理CORS问题

@EnableWebFluxSecurity
@Configuration
public class SecurityConfig {//安全拦截配置@Beanpublic SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) throws Exception {return http.cors(cors -> cors.configurationSource(corsConfigurationSource())).authorizeExchange(exchanges ->exchanges.pathMatchers("/**").permitAll().anyExchange().authenticated()).oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())).csrf(ServerHttpSecurity.CsrfSpec::disable).build();}@Beanpublic CorsConfigurationSource corsConfigurationSource() {CorsConfiguration corsConfig = new CorsConfiguration();corsConfig.addAllowedOriginPattern("*"); // 允许任何源corsConfig.addAllowedMethod("*"); // 允许任何HTTP方法corsConfig.addAllowedHeader("*"); // 允许任何HTTP头corsConfig.setAllowCredentials(true); // 允许证书(cookies)corsConfig.setMaxAge(3600L); // 预检请求的缓存时间(秒)UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", corsConfig); // 对所有路径应用这个配置return source;}
}

这里需要注意几点

  • 书接上文,这里虽然用了oauth2.jwt(Customizer.withDefaults()),但实际上基于远程auth微服务开放的jwkSetEndpoint配置的JwtDecoder
  • .cors(cors -> cors.configurationSource(corsConfigurationSource()))一次性处理CORS问题。

四、content微服务代码

1. controller

	@PreAuthorize("hasAuthority('xc_teachmanager_course_list')")@ApiResponse(responseCode = "200", description = "Successfully retrieved user")@Operation(summary = "查询课程信息列表")@PostMapping("/course/list")public PageResult<CourseBase> list(PageParams pageParams,@Parameter(description = "请求具体内容") @RequestBody(required = false) QueryCourseParamsDto dto){SecurityUtil.XcUser xcUser = SecurityUtil.getUser();if (xcUser != null){System.out.println(xcUser.getUsername());System.out.println(xcUser.toString());}return courseBaseInfoService.queryCourseBaseList(pageParams, dto);}

使用了@PreAuthorize("hasAuthority('xc_teachmanager_course_list')")修饰的controller资源。

2. SecurityConfig

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {//安全拦截配置@Beanpublic SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) ->authorize.requestMatchers("/**").permitAll().anyRequest().authenticated()).csrf(AbstractHttpConfigurer::disable).oauth2ResourceServer(oauth -> oauth.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtAuthenticationConverter())));return http.build();}private JwtAuthenticationConverter jwtAuthenticationConverter() {JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();jwtConverter.setJwtGrantedAuthoritiesConverter(jwt -> {// 从JWT的claims中提取权限信息List<String> authorities = jwt.getClaimAsStringList("authorities");if (authorities == null) {return Collections.emptyList();}return authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());});return jwtConverter;}
}

需要注意几点

  • 使用@EnableMethodSecurity@PreAuthorize生效
  • gateway一样,需要基于远程auth微服务开放的jwkSetEndpoint配置JwtDecoder
  • 指定JwtAuthenticationConverter ,让anyRequest().authenticated()需要验证的请求,除了完成默认的JWT验证外,还需要完成JwtAuthenticationConverter 指定逻辑。
  • JwtAuthenticationConverter 中将JWTauthorities部分形成数组后写入GrantedAuthorities,这正是spring security6用于校验@PreAuthorize的字段。

3. 解析JWT Utils

@Slf4j
public class SecurityUtil {public static XcUser getUser(){Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication == null){return null;}if (authentication instanceof JwtAuthenticationToken) {JwtAuthenticationToken jwtAuth = (JwtAuthenticationToken) authentication;System.out.println(jwtAuth);Map<String, Object> tokenAttributes = jwtAuth.getTokenAttributes();System.out.println(tokenAttributes);Object sub = tokenAttributes.get("sub");return JSON.parseObject(sub.toString(), XcUser.class);}return null;}@Datapublic static class XcUser implements Serializable {private static final long serialVersionUID = 1L;private String id;private String username;private String password;private String salt;private String name;private String nickname;private String wxUnionid;private String companyId;/*** 头像*/private String userpic;private String utype;private LocalDateTime birthday;private String sex;private String email;private String cellphone;private String qq;/*** 用户状态*/private String status;private LocalDateTime createTime;private LocalDateTime updateTime;}
}

JWT的信息解析回XcUser ,相当于用户携带JWT访问后端,后端可以根据JWT获取此用户的信息。当然,你可以尽情的自定义,扩展。

4. 总结

当用户携带JWT访问需要权限的资源时,现在可以正常的校验权限了。

五、一些坑

  1. RegisteredClient时注册那么多redirectUri是因为debug了很久,才发现获取授权码和获取JWT时,redirect_uri参数需要一致。
  2. cors问题,spring secuity6似乎会一开始直接默认拒绝cors,导致跨域请求刚到gateway就寄了,到不了content微服务,即使content微服务配置了CORS的处理方案,也无济于事。

在这里插入图片描述

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

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

相关文章

【51单片机】串口(江科大)

8.1串口通信 1.串口介绍 2.硬件电路 3.电平标准 电平标准是数据1和数据0的表达方式,是传输线缆中人为规定的电压与数据的对应关系,串口常用的电平标准有如下三种: 电平标准是数据1和数据O的表达方式,是传输线缆中人为规定的电 压与数据的对应关系,串口常用的电平标准有如下…

【QT+QGIS跨平台编译】之三十六:【RasterLite2+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、RasterLite2介绍二、文件下载三、文件分析四、pro文件五、编译实践一、RasterLite2介绍 RasterLite2是一个开源的轻量级栅格数据库,可以用于存储和管理各种类型的栅格数据,包括卫星遥感图像、数字高程模型等。 与传统的GIS数据存储方式不同,RasterLite2采用基…

React18原理: 时间分片技术选择

渲染1w个节点的不同方式 1 &#xff09;案例1&#xff1a;一次渲染1w个节点 <div idroot><div><script type"text/javascript">function randomHexColor() {return "#" ("0000" (Math.random() * 0x1000000 << 0).toS…

【深蓝学院】移动机器人运动规划--第4章 动力学约束下的运动规划--作业

文章目录 1. T11.1 题目1.2 求解1.3 Pontryagin Minimum Principle 的拓展 2. T22.1 题目2.2 求解 3. Reference 1. T1 1.1 题目 1.2 求解 1.3 Pontryagin Minimum Principle 的拓展 2. T2 2.1 题目 2.2 求解 Listing1&#xff1a; demo_node.cpp/trajectoryLibrary() for(i…

蓝桥杯-X图形

问题描述 给定一个字母矩阵。一个 X 图形由中心点和由中心点向四个 45度斜线方向引出的直线段组成&#xff0c;四条线段的长度相同&#xff0c;而且四条线段上的字母和中心点的字母相同。 一个 X 图形可以使用三个整数 r,c,L 来描述&#xff0c;其中 r,c 表示中心点位于第 r 行…

理解JAVA EE设计模式

理解JAVA EE设计模式 在Web应用程序的设计和开发阶段,开发人员在开发类似的项目时可能会遇到相似的问题。每名开发人员可能会遇到的问题找出不同或相似的解决方案。但是,这导致一些时间和精力浪费在为相似的问题寻找解决方案上。因此,要啊节省时间和精力,需要记录常见问题…

Vulnhub靶场 DC-9

目录 一、环境搭建 二、信息收集 1、主机发现 2、指纹识别 三、漏洞复现 1、dirsearch目录探测 2、sqlmap注入测试 3、文件包含漏洞 4、Knockd敲门服务 5、ssh爆破 ​​​​​​​6、提权 四、提取flag 一、环境搭建 Vulnhub靶机下载&#xff1a; 官网地址&#xff1a;https://…

红队打靶练习:Alfa:1

下载连接点击此处即可&#xff01; 目录 信息收集 1、arp 2、nmap 3、gobuster WEB web信息收集 FTP登录 smaba服务 crunch密码生成 提权 系统信息收集 权限提升 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# arp-scan -l Interface: eth0, type: EN10MB, …

消息中间件面试篇

消息中间件 RabbitMQ 消息不丢失 可能导致消息丢失的情况&#xff1a; 生产者发送消息丢失消息队列宕机消费者服务宕机&#xff0c;未接收到消息 生产者确认机制 该机制解决了生产者发送消息有可能丢失的问题。 RabbitMQ提供了publisher confirm机制来避免消息发送到MQ过程…

Unity下使用Sqlite

sqlite和access类似是文件形式的数据库&#xff0c;不需要安装任何服务&#xff0c;可以存储数据&#xff0c;使用起来还是挺方便的。 首先需要安装DLL 需要的DLL 我们找到下面两个文件放入Plugins目录 Mono.Data.Sqlite.dll System.Data.dll DLL文件位于Unity的安装目录下的…

LeetCode、1143. 最长公共子序列【中等,二维DP】

文章目录 前言LeetCode、1143. 最长公共子序列【中等&#xff0c;二维DP】题目链接与分类思路2022年暑假学习思路及题解二维DP解决 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者…

python+django咖啡网上商城网站

全网站共设计首页、咖啡文化、咖啡商城、个人信息、联系我们5个栏目以及登录、注册界面&#xff0c;让用户能够全面的了解中国咖啡咖啡文化宣传网站以及一些咖啡知识、文化。 栏目一首页&#xff0c;主要放置咖啡的起源及发展进程的图文介绍&#xff1b;栏目二咖啡文化&#xf…

第三百二十三回

文章目录 1. 概念介绍2. 使用方法2.1 基本用法2.2 缓冲原理 3. 示例代码4. 内容总结 我们在上一章回中介绍了"FadeInImage组件"相关的内容&#xff0c;本章回中将介绍CachedNetworkImage组件.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1. 概念介绍 我们在本章…

InnoDB主键索引的B+Tree高度是多少?存储数据量是多少?

BTree示意图如下&#xff1a; 图片来源&#xff1a;BTree和BTree详解_bbrtt的结构图-CSDN博客 假设一行数据大小是1k&#xff0c;一页可以存储16行这样的数据。InnoDB的指针占用6个字节的空间&#xff0c;主键假设为bigint&#xff0c;占用字节数为8&#xff0c;那么可得公式…

Day 44 | 动态规划 完全背包、518. 零钱兑换 II 、 377. 组合总和 Ⅳ

完全背包 题目 文章讲解 视频讲解 完全背包和0-1背包的区别在于&#xff1a;物品是否可以重复使用 思路&#xff1a;对于完全背包问题&#xff0c;内层循环的遍历方式应该是从weight[i]开始一直遍历到V&#xff0c;而不是从V到weight[i]。这样可以确保每种物品可以被选择多次…

unity 点击事件

目录 点击按钮&#xff0c;显示图片功能教程 第1步添加ui button&#xff0c;添加ui RawImage 第2步 添加脚本&#xff1a; 第3步&#xff0c;把脚本拖拽到button&#xff0c;点击button&#xff0c;设置脚本的变量&#xff0c; GameObject添加 Component组件 点击按钮&am…

如何才能学好JVM?——零基础入门篇

1. JVM是什么&#xff1f; JVM是Java Virtual Machine的简称&#xff0c;它是一个虚拟的计算机&#xff0c;专门为执行Java程序而设计。 你可以想象它是一个能够运行Java字节码的平台&#xff0c;无论你的程序在Windows、Mac还是Linux上&#xff0c;它们都能通过JVM在这些系统…

linuxqq关闭主面板后无法再次打开的问题

文章目录 前言解决方案强调一点 前言 听说QQ出了linux版&#xff0c;所以来试试。结果试试就逝世。这次记录一个关闭后没办法打开的解决办法。 解决方案 刚安装好后如果点了关闭&#xff0c;系统托盘里也没有&#xff0c;点击图标又是重新登录。当然&#xff0c;我们最简单、…

C语言—简单的if语句

1.输入你的身高和体重&#xff0c;测试你的健康状况。 计算bmi的值&#xff0c; bmi &#xff08;体重/身高的平方) 如果bmi 小于18.5&#xff0c;则显示“偏瘦&#xff0c;注意加强营养” 如果bmi 在18.…

Paper - VQGAN: Taming Transformers for High-Resolution Image Synthesis 简读

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/136055085 VQGAN: Taming Transformers for High-Resolution Image Synthesis, CVPR 2021 VQGAN: 改良 Transformer 模型以实现高清图像合成 源码…