微服务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…

「计算机网络」物理层

物理层的基本概念 物理层的作用&#xff1a;尽可能屏蔽掉不同传输媒体和通信手段的差异物理层规程&#xff1a;用于物理层的协议主要任务&#xff1a;确定与传输媒体的接口有关的一些特性 机械特性电器特性功能特性过程特性 数据通信的基础知识 数据通信系统的模型 划分为…

【深蓝学院】移动机器人运动规划--第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 行…

LOL全英文界面

文章目录 前言.bat 前言 对于需要学英文的同学们来说, 玩起lol来 ,有全部界面文字都是英文. 那真是很酷对于那些改配置还不起作用的玩家来说, 我只告诉你们, 只需要写一个.bat文件, 当然我的lol是日服版. 抗日起来是非常 cool .bat 只有两行 pushd "K:\Program Files (…

Spring Security 弃用 WebSecurityConfigurerAdapter 重写登录接口 前后端分离 返回json数据格式

springboot 版本高于 2.7 之后 弃用了 WebSecurityConfigurerAdapter 推荐使用组件化配置安全组件。 原版本的2.7版本的登录接口 功能&#xff1a; 通过/api/doLogin 进行登录 package cn.devops.config;import cn.devops.model.User; import cn.devops.response.RespBe…

nginx命名location跳转的模块上下文继承

目录 1. 缘起2. 解决方案2.1 保留指定模块的上下文信息2.2 获取指定模块的上下文信息2.3 设置指定模块的上下文信息2.4 设置模块上下文是否需要继承标记2.5 对openrety lua代码的支持 1. 缘起 nginx提供了非常棒的功能&#xff0c;命名location&#xff0c;如文章nginx的locati…

理解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的安装目录下的…

「MySQL」事务

简介 定义&#xff1a;事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败 基本操作 方式1 查看/设置事务提交方式 select…

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

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

Day32 贪心算法part02

买卖股票的最佳时机 太牛了我&#xff0c;随随便便双指针秒杀 md题解里面双指针都没用直接for循环秒杀 跳跃游戏 写成这样纯粹是没有看到第一次跳跃必须从第一个开始 class Solution:def canJump(self, nums: List[int]) -> bool:if len(nums) 1:return Truefor i in …

autojs自动化刷视频脚本

视频展示 视频 //悬浮窗 // var window floaty.rawWindow( // <frame gravity"center" bg"#ff00ff"> // <button id"action" w"300dp" h"300dp"> // 按钮 // </button> // </fram…

python+django咖啡网上商城网站

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

第三百二十三回

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