当前位置: 首页 > news >正文

JWT介绍和结合springboot项目实践(登录、注销授权认证管理)

目录

    • 一、JWT介绍
    • (一)基本介绍
    • (二)jwt有哪些库
      • 1、jjwt(Java JWT)
      • 2、nimbus - jwt - jwt - api 和 nimbus - jwt - jwt - impl
      • 3、spring - security - jwt(已弃用,但在旧项目中有参考价值)
    • 二、结合springboot项目运用
    • (一)整合JWT
      • 1、添加依赖
      • 2、配置JWT相关属性
      • 3、创建JWT工具类
      • 4、创建认证过滤器(用于保护资源)
      • 5. 配置 Spring Security(如果项目使用 Spring Security)
    • (二)结合用户登录、注销操作(完整过程)
      • 1. 用户实体类(示例)
      • 2. 用户服务层(UserService)
      • 3. 用户详细信息服务(UserDetailsService)实现
      • 4. 登录接口
      • 5. 注销接口
      • 6. 完善 JwtAuthenticationFilter(处理 JWT 验证及授权)
      • 7. 配置类调整(SecurityConfig)
    • (三)登录认证过程具体分析
      • 1、配置AuthenticationManager
      • 2、用户认证过程
    • (四)登录成功后,后续请求认证过程具体分析
      • 1、JwtAuthenticationFilter 的作用
      • 2、加载用户详细信息和授权
    • (五)首次请求和后续请求的区别
      • 1、首次请求(登录成功后首次携带 JWT 访问)
      • 2、后续请求(已完成首次加载用户信息后的请求)

一、JWT介绍

(一)基本介绍

JSON Web Token 是一种开放标准(RFC 7519),用于在各方之间以 JSON 对象的形式安全地传输信息。它可以在无状态、分布式的应用环境中实现身份验证和授权等功能,通常由三部分组成,分别是头部(Header)、载荷(Payload)和签名(Signature),格式为 Header.Payload.Signature。在应用中配置 JWT 相关内容,主要涉及设置私钥(用于签名生成)等关键信息,以便后续能正确地创建、验证 JWT

(二)jwt有哪些库

1、jjwt(Java JWT)

简介:这是一个非常流行的 Java 库,用于处理 JWT。它提供了简洁的 API 来生成和解析 JWT。它支持多种 JWT 签名算法,如 HS256、RS256 等,并且可以方便地在 Java 项目(特别是 Spring Boot 项目)中进行集成。
需要引入的依赖

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt - api</artifactId><version>0.11.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt - impl</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt - jackson</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>

示例代码:
生成 JWT:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;public class JwtExample {private static final String SECRET_KEY = "your - secret - key";public static String generateJwt(String subject, long expirationMillis) {Map<String, Object> claims = new HashMap<>();Date now = new Date();Date expiration = new Date(now.getTime() + expirationMillis);return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(now).setExpiration(expiration).signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();}public static Claims parseJwt(String jwt) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(jwt).getBody();}
}

2、nimbus - jwt - jwt - api 和 nimbus - jwt - jwt - impl

简介:Nimbus JOSE + JWT 是一个功能强大的库,用于处理 JWT 以及其他 JOSE(JSON Object Signing and Encryption)结构。nimbus - jwt - jwt - api提供了接口定义,nimbus - jwt - jwt - impl是其实现部分。它支持高级的 JWT 功能,如加密的 JWT(JWE),并且在安全和标准遵循方面表现出色。
需要引入的依赖

<dependency><groupId>com.nimbusds</groupId><artifactId>nimbus - jwt - jwt - api</artifactId><version>9.22</version>
</dependency>
<dependency><groupId>com.nimbusds</groupId><artifactId>nimbus - jwt - jwt - impl</artifactId><version>9.22</version>
</dependency>

示例代码:
生成 JWT:

import com.nimbusds.jose.JOSEException;
import com.nimbusds.jose.JWSAlgorithm;
import com.nimbusds.jose.JWSHeader;
import com.nimbusds.jose.JWSSigner;
import com.nimbusds.jose.crypto.MACSigner;
import com.nimbusds.jwt.JWTClaimsSet;
import com.nimbusds.jwt.SignedJWT;
import java.text.ParseException;
import java.util.Date;public class NimbusJwtExample {private static final String SECRET_KEY = "your - secret - key";public static String generateJwt(String subject, long expirationMillis) throws JOSEException {Date now = new Date();Date expiration = new Date(now.getTime() + expirationMillis);JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().subject(subject).issueTime(now).expirationTime(expiration).build();JWSHeader header = new JWSHeader(JWSAlgorithm.HS256);SignedJWT signedJWT = new SignedJWT(header, claimsSet);JWSSigner signer = new MACSigner(SECRET_KEY);signedJWT.sign(signer);return signedJWT.serialize();}public static void parseJwt(String jwt) throws ParseException, JOSEException {SignedJWT signedJWT = SignedJWT.parse(jwt);if (signedJWT.verify(new MACSigner(SECRET_KEY))) {JWTClaimsSet claims = signedJWT.getJWTClaimsSet();System.out.println("Subject: " + claims.getSubject());System.out.println("Expiration Time: " + claims.getExpirationTime());}}
}

3、spring - security - jwt(已弃用,但在旧项目中有参考价值)

简介:这是 Spring Security 官方之前提供的用于 JWT 集成的库,不过现在已经弃用。它在早期的 Spring Boot 项目与 JWT 集成中发挥了作用,并且提供了基于 Spring Security 框架的 JWT 认证机制。
需要引入的依赖

<dependency><groupId>org.springframework.security</groupId><artifactId>spring - security - jwt</artifactId><version>1.1.0.RELEASE</version>
</dependency>

示例(简单示意其曾经的使用方式):
在配置类中(部分代码):

import org.springframework.beans.factory.annotation.Autowired;
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.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.BCryptPasswordCipher;
import org.springframework.security.crypto.password.PasswordCipher;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.web.filter.OncePerRequestFilter;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate JwtAuthenticationEntryPoint unauthorizedHandler;@Beanpublic PasswordCipher passwordCipher() {return new BCryptPasswordCipher();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/api/public/**").permitAll().anyRequest().authenticated().and().exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and().addFilterBefore(new JwtAuthenticationFilter(userDetailsService), UsernamePasswordAuthenticationFilter.class);}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordCipher(passwordCipher());}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}

这个库中的JwtAuthenticationFilter等组件(这里只是示意代码)可以帮助实现 JWT 的验证和基于 JWT 的用户认证流程,不过在新项目中一般推荐使用jjwt等更现代的库与 Spring Boot 的安全配置相结合来实现 JWT 认证。

二、结合springboot项目运用

以下示例使用 Java 语言,结合 jjwt 库(一个常用的用于处理 JWT 的库)来展示如何基于配置的私钥进行 JWT 的生成和验证操作。

(一)整合JWT

1、添加依赖

如果使用 Maven 构建项目,需要在pom.xml文件中添加jjwt相关依赖。jjwt是一个用于处理 JWT 的 Java 库,提供了生成和解析 JWT 的功能。

<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt - api</artifactId><version>0.11.5</version>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt - impl</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt - jackson</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>

2、配置JWT相关属性

可以在application.yml或application.properties文件中配置 JWT 相关的属性,如密钥、过期时间等。例如:

jwt:secret: your - secret - key - hereexpiration: 86400000 # 过期时间,单位为毫秒,这里设置为一天

3、创建JWT工具类

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Component
public class JwtUtils {@Value("${jwt.secret}")private String secret;@Value("${jwt.expiration}")private long expiration;// 生成JWTpublic String generateToken(String subject) {Map<String, Object> claims = new HashMap<>();return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() + expiration)).signWith(SignatureAlgorithm.HS256, secret).compact();}// 从JWT中获取主题(通常是用户信息)public String getSubjectFromToken(String token) {Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();return claims.getSubject();}// 验证JWT是否有效public boolean validateToken(String token) {try {Jwts.parser().setSigningKey(secret).parseClaimsJws(token);return true;} catch (Exception e) {return false;}}
}
在这个工具类中:
通过@Value注解从配置文件中获取 JWT 的密钥和过期时间。
generateToken方法用于生成 JWT,它创建一个包含声明(Claims)的JWT,设置主题(通常是用户标识等信息)、签发时间和过期时间,并使用配置的密钥和HS256签名算法进行签名。
getSubjectFromToken方法用于从 JWT 中获取主题,通过解析 JWT 并获取Claims对象中的主题信息。
validateToken方法用于验证 JWT 的有效性,尝试解析 JWT,如果没有抛出异常则表示 JWT 有效。

4、创建认证过滤器(用于保护资源)

创建一个过滤器来拦截请求并验证 JWT。例如,在 Spring Boot 的 Web 应用中可以创建一个JwtAuthenticationFilter:

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {@Autowiredprivate JwtUtils jwtUtils;@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {String token = getTokenFromRequest(request);if (token!= null && jwtUtils.validateToken(token)) {String username = jwtUtils.getSubjectFromToken(token);UserDetails userDetails = userDetailsService.loadUserDetails(username);UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authentication);}filterChain.doFilter(request, response);}private String getTokenFromRequest(HttpServletRequest request) {String bearerToken = request.getHeader("Authorization");if (bearerToken!= null && bearerToken.startsWith("Bearer ")) {return bearerToken.substring(7);}return null;}
}
在这个过滤器中:
首先通过getTokenFromRequest方法从请求头中获取 JWT(通常在Authorization头中,格式为Bearer <token>)。
如果获取到 JWT 并且通过JwtUtils验证有效,则从 JWT 中获取用户信息(主题),通过UserDetailsService加载用户详细信息,创建一个UsernamePasswordAuthenticationToken用于 Spring Security 的认证,并将其设置到SecurityContextHolder中,这样后续的请求处理就能获取到认证后的用户信息。
最后通过filterChain.doFilter(request, response)让请求继续向下传递。

5. 配置 Spring Security(如果项目使用 Spring Security)

在 Spring Security 的配置类中,需要配置认证管理器(AuthenticationManager)和添加自定义的过滤器。以下是一个简单的示例:

import org.springframework.beans.factory.annotation.Autowired;
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.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.BCryptPasswordCipher;
import org.springframework.security.crypto.password.PasswordCipher;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate JwtAuthenticationFilter jwtAuthenticationFilter;@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/api/public/**").permitAll().anyRequest().authenticated();http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordCipher(passwordCipher());}@Beanpublic PasswordCipher passwordCipher() {return new BCryptPasswordCipher();}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}
在这个配置类中:
configure(HttpSecurity http)方法:
禁用了 CSRF(跨站请求伪造)保护,因为 JWT 是无状态的,不需要依赖会话。
设置会话管理策略为STATELESS,表示不使用基于会话的认证。
配置了请求的授权规则,例如/api/public/**路径下的请求可以被任何人访问,其他请求需要认证。
添加了自定义的JwtAuthenticationFilter过滤器,并且放在UsernamePasswordAuthenticationFilter之前,这样在进行用户名 - 密码认证之前就会先验证 JWT。
configure(AuthenticationManagerBuilder auth)方法用于配置认证管理器,指定用户详细信息服务(UserDetailsService)和密码加密方式(这里使用BCryptPasswordCipher)。
passwordCipher方法创建了一个BCryptPasswordCipher用于密码加密。
authenticationManagerBean方法用于暴露AuthenticationManager bean,供其他部分(如JwtAuthenticationFilter)使用

(二)结合用户登录、注销操作(完整过程)

1. 用户实体类(示例)

首先,创建一个简单的用户实体类,用于表示系统中的用户信息,假设用户有用户名、密码等基本属性,并且实现 UserDetails 接口(方便与 Spring Security 集成)。

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleAuthority;
import org.springframework.security.core.userdetails.UserDetails;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;public class User implements UserDetails {private String username;private String password;private List<String> roles; // 假设用户有角色列表,用于权限控制public User(String username, String password, List<String> roles) {this.username = username;this.password = password;this.roles = roles;}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {List<GrantedAuthority> authorities = new ArrayList<>();for (String role : roles) {authorities.add(new SimpleAuthority(role));}return authorities;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

2. 用户服务层(UserService)

import org.springframework.stereotype.Service;import java.util.List;public interface UserService {User findByUsername(String username);
}@Service
public class UserServiceImpl implements UserService {// 这里模拟从数据库或者其他数据源获取用户信息,实际应用中需要替换为真实的数据查询逻辑@Overridepublic User findByUsername(String username) {// 假设数据库中有如下用户信息(仅为示例,真实场景需从数据库获取)if ("admin".equals(username)) {List<String> roles = List.of("ROLE_ADMIN");return new User("admin", "$2a$10$pY85s8XzG7f2m8Y18T5c7e6c7888g5X889g9W89c8g", roles);}return null;}
}

3. 用户详细信息服务(UserDetailsService)实现

实现 Spring Security 的 UserDetailsService 接口,用于加载用户详细信息供认证使用,它会调用上面的 UserService 来获取用户数据。

import org.springframework.beans.factory.annotation.Autowired;
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.Service;@Service
public class CustomUserDetailsService implements UserDetailsService {@Autowiredprivate UserService userService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userService.findByUsername(username);if (user == null) {throw new UsernameNotFoundException("用户不存在");}return user;}
}

4. 登录接口

创建一个登录接口,用于用户提交用户名和密码进行登录,成功登录后生成并返回 JWT 给客户端。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;@RestController
@RequestMapping("/api/auth")
public class AuthController {@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate JwtUtils jwtUtils;@PostMapping("/login")public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest, HttpServletResponse response) {Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));SecurityContextHolder.getContext().setAuthentication(authentication);String jwt = jwtUtils.generateToken(loginRequest.getUsername());Map<String, String> tokenResponse = new HashMap<>();tokenResponse.put("token", jwt);return new ResponseEntity<>(tokenResponse, HttpStatus.OK);}// 登录请求的参数封装类static class LoginRequest {private String username;private String password;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}}
}
在登录接口中:
通过 AuthenticationManager 对用户提交的用户名和密码进行认证,如果认证成功,则将认证信息设置到 SecurityContextHolder 中,表示用户已登录。
然后使用 JwtUtils 工具类生成 JWT,并将其封装到响应中返回给客户端,客户端后续的请求就可以携带这个 JWT 来访问受保护的资源。

5. 注销接口

创建注销接口,用于清除用户的认证状态以及使 JWT 失效(这里使 JWT 失效可以通过一些策略实现,比如将 JWT 加入黑名单等,下面是简单示例通过清除 SecurityContextHolder 中的认证信息来模拟注销效果)。

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/api/auth")
public class AuthController {// 省略其他代码,只展示注销接口相关部分@PostMapping("/logout")public ResponseEntity<?> logout() {SecurityContextHolder.clearContext();return new ResponseEntity<>(HttpStatus.OK);}
}
当用户调用注销接口时,通过 SecurityContextHolder.clearContext() 清除了当前用户的认证上下文信息,这样后续的请求就会被视为未认证状态,相当于完成了注销操作。

6. 完善 JwtAuthenticationFilter(处理 JWT 验证及授权)

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {@Autowiredprivate JwtUtils jwtUtils;@Autowiredprivate UserDetailsService userDetailsService;// 假设存在一个 JWT 黑名单列表,用于存储已注销或失效的 JWT(这里简单用 List 模拟,实际可使用 Redis 等存储)@Autowiredprivate List<String> jwtBlacklist;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)throws ServletException, IOException {String token = getTokenFromRequest(request);if (token!= null && jwtUtils.validateToken(token) &&!jwtBlacklist.contains(token)) {String username = jwtUtils.getSubjectFromToken(token);UserDetails userDetails = userDetailsService.loadUserDetails(username);UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authentication);}filterChain.doFilter(request, response);}private String getTokenFromRequest(HttpServletRequest request) {String bearerToken = request.getHeader("Authorization");if (bearerToken!= null && bearerToken.startsWith("Bearer ")) {return bearerToken.substring(7);}return null;}
}
在这个过滤器中,添加了对 jwtBlacklist 的检查,如果请求携带的 JWT 在黑名单中,即使它本身验证签名等是有效的,也会被视为无效的 JWT,不会进行后续的授权操作,从而实现了在注销或者其他使 JWT 失效场景下的安全控制。

7. 配置类调整(SecurityConfig)

根据上述新增的登录、注销接口以及完善的 JWT 验证逻辑,对 Spring Security 配置类进行相应调整,例如配置登录、注销相关的请求路径权限等。

import org.springframework.beans.factory.annotation.Autowired;
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.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.BCryptPasswordCipher;
import org.springframework.security.crypto.password.PasswordCipher;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate JwtAuthenticationFilter jwtAuthenticationFilter;@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/api/auth/login", "/api/auth/logout").permitAll() // 允许登录、注销接口无需认证访问.antMatchers("/api/public/**").permitAll().anyRequest().authenticated();http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordCipher(passwordCipher());}@Beanpublic PasswordCipher passwordCipher() {return new BCryptPasswordCipher();}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}

通过以上完整的代码示例,在 Spring Boot 项目中实现了基于 JWT 的身份认证机制,并且结合了用户登录、注销操作,提供了更符合实际应用场景的安全认证授权流程。需要注意的是,上述代码中的部分内容(如用户数据查询、JWT 黑名单存储等)只是简单的示例,在实际生产环境中,需要根据具体的架构和业务需求进行更深入、安全的设计和实现。

(三)登录认证过程具体分析

1、配置AuthenticationManager

在SecurityConfig类中,通过重写configure(AuthenticationManagerBuilder auth)方法来配置AuthenticationManager。
代码如下:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordCipher(passwordCipher());
}

这里将自定义的UserDetailsService(即CustomUserDetailsService)设置给AuthenticationManagerBuilder。UserDetailsService负责根据用户名加载用户详细信息(包括密码等)。同时,设置了密码编码器passwordCipher()(这里是BCryptPasswordCipher),用于验证用户提交的密码与数据库中存储的密码是否匹配。

2、用户认证过程

2.1 当调用登录接口时,AuthController中的login方法被触发。
2.2 首先创建UsernamePasswordAuthenticationToken:
代码为new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())。这一步将用户提交的用户名和密码封装成一个认证令牌,作为即将进行认证的凭据。
2.3 然后将这个令牌传递给AuthenticationManager的authenticate方法:
代码是authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()))。
2.4 AuthenticationManager会根据配置的UserDetailsService来加载用户详细信息。具体来说,CustomUserDetailsService的loadUserByUsername方法会被调用,该方法会根据用户名查找用户信息。如果找不到用户(User对象为null),会抛出UsernameNotFoundException。
2.5 找到用户后,AuthenticationManager会使用配置的密码编码器(BCryptPasswordCipher)来验证用户提交的密码与数据库中存储的密码是否匹配。如果密码不匹配,会抛出BadCredentialsException等认证异常。
2.6 只有当用户名存在且密码匹配时,认证才会成功,AuthenticationManager会返回一个经过认证的Authentication对象。这个对象包含了用户的详细信息(如权限等),并且会被设置到SecurityContextHolder中(通过SecurityContextHolder.getContext().setAuthentication(authentication);),表示用户已经成功认证登录。后续流程(如生成 JWT)就可以基于这个已认证的状态进行操作。

(四)登录成功后,后续请求认证过程具体分析

1、JwtAuthenticationFilter 的作用

在服务端,JwtAuthenticationFilter起着关键的验证作用。这个过滤器会拦截请求,检查请求头中的Authorization字段是否包含有效的 JWT。
它通过getTokenFromRequest方法从请求头中提取 JWT,代码如下:

private String getTokenFromRequest(HttpServletRequest request) {String bearerToken = request.getHeader("Authorization");if (bearerToken!= null && bearerToken.startsWith("Bearer ")) {return bearerToken.substring(7);}return null;
}

如果提取到 JWT,就会调用jwtUtils.validateToken(token)方法来验证 JWT 的有效性。jwtUtils是之前创建的 JWT 工具类,validateToken方法会根据配置的密钥和签名算法检查 JWT 的签名是否正确,以及检查 JWT 是否过期等。

2、加载用户详细信息和授权

如果 JWT 验证通过,JwtAuthenticationFilter会从 JWT 中获取主题(通常是用户名),通过jwtUtils.getSubjectFromToken(token)方法实现。
然后,它会使用userDetailsService.loadUserDetails(username)加载用户详细信息。userDetailsService是CustomUserDetailsService,会根据用户名查找用户信息,包括用户的权限等信息。
最后,创建一个UsernamePasswordAuthenticationToken实例,将用户详细信息、凭证(这里设为null)和用户权限放入其中,代码如下:

UserDetails userDetails = userDetailsService.loadUserDetails(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null,userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);

并将这个认证令牌设置到SecurityContextHolder中,这样后续的请求处理就可以获取到已认证的用户信息,完成授权过程,允许用户访问受保护的资源。整个过程是无状态的,每次请求都通过 JWT 验证和加载用户信息来确定用户的身份和权限。

(五)首次请求和后续请求的区别

1、首次请求(登录成功后首次携带 JWT 访问)

首次请求在验证 JWT 成功后,会加载用户详细信息和进行授权。
当用户登录成功并获得 JWT 后,第一次使用该 JWT 访问受保护资源时,JwtAuthenticationFilter会拦截请求。它首先验证 JWT 的有效性,这包括检查签名是否正确以及是否过期等。
在 JWT 验证通过后,会从 JWT 中获取主题(通常是用户名),然后通过userDetailsService.loadUserDetails(username)加载用户详细信息。这个过程会从数据源(如数据库)中获取用户的完整信息,包括权限信息等。接着,会创建一个UsernamePasswordAuthenticationToken,将用户详细信息、凭证(一般设为null,因为 JWT 已经验证了身份)和用户权限放入其中,并将这个认证令牌设置到SecurityContextHolder中。这一步完成了用户的授权,使得后续的请求处理可以获取到已认证的用户信息,从而允许用户访问受保护的资源。

2、后续请求(已完成首次加载用户信息后的请求)

后续请求主要是验证 JWT。因为SecurityContextHolder已经保存了用户的认证信息,只要 JWT 验证通过,就可以直接使用已保存的用户授权信息来处理请求。
每次请求依然会经过JwtAuthenticationFilter,它会提取请求头中的 JWT 并验证。如果 JWT 验证通过,就会根据SecurityContextHolder中已有的用户认证信息来处理请求,不需要再次加载用户详细信息(除非SecurityContextHolder中的信息丢失或者过期等异常情况)。这样可以提高性能,因为避免了每次都从数据源重新加载用户信息的开销。不过,如果在请求处理过程中需要更新用户权限等信息,可能需要重新加载用户详细信息并更新SecurityContextHolder中的内容。

相关文章:

JWT介绍和结合springboot项目实践(登录、注销授权认证管理)

目录 一、JWT介绍&#xff08;一&#xff09;基本介绍&#xff08;二&#xff09;jwt有哪些库1、jjwt&#xff08;Java JWT&#xff09;2、nimbus - jwt - jwt - api 和 nimbus - jwt - jwt - impl3、spring - security - jwt&#xff08;已弃用&#xff0c;但在旧项目中有参考…...

Linux 下安装 Golang环境

Linux 下安装 Golang 获取Golang下载地址 安装 进入终端&#xff0c;登入root来到应用安装目录使用 wget 下载解压文件配置环境变量查看golang版本&#xff0c;测试是否配置成功GO设置代理环境变量 本篇教程 以 centos7 为环境基础 不使用软件包管理器安装&#xff0c;原因&am…...

「Mac畅玩鸿蒙与硬件36」UI互动应用篇13 - 数字滚动抽奖器

本篇将带你实现一个简单的数字滚动抽奖器。用户点击按钮后&#xff0c;屏幕上的数字会以滚动动画的形式随机变动&#xff0c;最终显示一个抽奖数字。这个项目展示了如何结合定时器、状态管理和动画实现一个有趣的互动应用。 关键词 UI互动应用数字滚动动画效果状态管理用户交…...

安装使用Ubuntu18.04超级大全集最初版(anaconda,pycharm,代理,c/c++环境)

本文介绍ubuntu1804中我目前用到的环境的完整配置&#xff0c;包括ubuntu安装软件&#xff0c;更新环境变量等都有涉及。图片非常多&#xff0c;能给的连接和材料都给了。希望能帮助到新同学。 目录 目录 目录 环境及镜像文件 安装Ubuntu ​编辑 开机之后 ​编辑 更新…...

Redis设计与实现第16章 -- Sentinel 总结1(初始化、主从服务器获取信息、发送信息、接收信息)

Sentinel是Redis的高可用解决方案&#xff1a;由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器&#xff0c;以及这些主服务器属下的所有从服务器&#xff0c;被监视的主服务器进入下线状态时&#xff0c;自动将下线主服务器属下的某个从服务器升级为新的主…...

ChatGPT科研应用、论文写作、课题申报、数据分析与AI绘图

随着人工智能技术的飞速发展&#xff0c;ChatGPT等先进语言模型正深刻改变着科研工作的面貌。从科研灵感的激发、论文的高效撰写&#xff0c;到课题的成功申报&#xff0c;乃至复杂数据的深度分析与可视化呈现&#xff0c;AI技术均展现出前所未有的潜力。其实众多科研前沿工作者…...

OceanBase数据库系列之:基于docker快速安装OceanBase数据库,基于linux服务器快速部署OceanBase数据库

OceanBase数据库系列之&#xff1a;基于docker快速安装OceanBase数据库&#xff0c;基于linux服务器快速部署OceanBase数据库 一、docker快速安装OceanBase数据库下载OceanBase数据库镜像查看镜像启动OceanBase数据库查看OceanBase数据库是否启动成功 二、基于linux部署OceanBa…...

无星的微前端之旅(四)——qiankun线上服务代理到本地

这个方式其实是我在上家公司的时候体验过&#xff0c;觉得确实很有意思。 所以这里来逆推一下实现方式。 解决了什么痛点 1.开发一个模块&#xff0c;需要启动2-3个项目 在微前端的开发过程中&#xff0c;如果我们要在主应用中看效果&#xff0c;我们至少需要启动一个主应用&am…...

鸿蒙进阶篇-Stage模型、UIAbility

“在科技的浪潮中&#xff0c;鸿蒙操作系统宛如一颗璀璨的新星&#xff0c;引领着创新的方向。作为鸿蒙开天组&#xff0c;今天我们将一同踏上鸿蒙基础的探索之旅&#xff0c;为您揭开这一神奇系统的神秘面纱。” 各位小伙伴们我们又见面了,我就是鸿蒙开天组,下面让我们进入今…...

快速上手:如何开发一个实用的 Edge 插件

在日常浏览网页时&#xff0c;背景图片能够显著提升网页的视觉体验。如果你也想为自己的浏览器页面添加个性化背景图片&#xff0c;并希望背景图片设置能够持久保存&#xff0c;本文将介绍如何通过开发一个自定义Edge插件来实现这一功能。我们将涵盖保存背景设置到插件选项页&a…...

java缓存技术点介绍

1. 缓存&#xff08;Cache&#xff09;&#xff1a; • 缓存是指用于存储数据的临时存储区域&#xff0c;以便快速访问。在Java中&#xff0c;缓存通常用于存储频繁访问的对象、结果集或其他数据。 2. 缓存命中率&#xff08;Cache Hit Ratio&#xff09;&#xff1a; • 缓存命…...

【单片机毕业设计12-基于stm32c8t6的智能称重系统设计】

【单片机毕业设计12-基于stm32c8t6的智能称重系统设计】 前言一、功能介绍二、硬件部分三、软件部分总结 前言 &#x1f525;这里是小殷学长&#xff0c;单片机毕业设计篇12-基于stm32c8t6的智能称重系统设计 &#x1f9ff;创作不易&#xff0c;拒绝白嫖可私 一、功能介绍 ----…...

音视频流媒体直播/点播系统EasyDSS互联网视频云平台介绍

随着互联网技术的飞速发展&#xff0c;音视频流媒体直播已成为现代社会信息传递与娱乐消费的重要组成部分。在这样的背景下&#xff0c;EasyDSS互联网视频云平台应运而生&#xff0c;它以高效、稳定、便捷的特性&#xff0c;为音视频流媒体直播领域带来了全新的解决方案。 1、产…...

3dtile平移子模型以及修改 3D Tiles 模型的模型矩阵z平移

第一段代码&#xff1a;应用平移变换到子模型 这段代码的目的是获取子模型的变换矩阵&#xff0c;并将其平移 10 个单位。 if (submodel) {// 获取当前子模型的变换矩阵let transform submodel.transform// 创建一个向上的平移矩阵&#xff0c;平移 10 个单位let translation…...

JavaScript中类数组对象及其与数组的关系

JavaScript中类数组对象及其与数组的关系 1. 什么是类数组对象&#xff1f; 类数组对象是指那些具有 length 属性且可以通过非负整数索引访问元素的对象。虽然这些对象看起来像数组&#xff0c;但它们并不具备真正数组的所有特性&#xff0c;例如没有继承 Array.prototype 上…...

【机器学习】机器学习学习笔记 - 监督学习 - 逻辑回归分类朴素贝叶斯分类支持向量机 SVM (可分类、可回归) - 04

逻辑回归分类 import numpy as np from sklearn import linear_modelX np.array([[4, 7], [3.5, 8], [3.1, 6.2], [0.5, 1], [1, 2], [1.2, 1.9], [6, 2], [5.7, 1.5], [5.4, 2.2]]) y np.array([0, 0, 0, 1, 1, 1, 2, 2, 2])# 逻辑回归分类器 # solver&#xff1a;求解器&a…...

Python脚本文件开头两行#!/usr/bin/python和# -*- coding: utf-8 -*-的作用

Linux环境下&#xff0c;Python脚本代码文件&#xff0c;比如test.py&#xff0c;文件的第一行一般要指定解释器&#xff0c;使用Linux的Shebang的形式指定&#xff0c;第二行一般指定编码格式。 首行指定解释器工具&#xff0c;使用方式如下&#xff1a; # 第1种方式&#x…...

使用PyQt5开发一个GUI程序的实例演示

一、安装Python 下载安装到这个目录 G:\Python38-32 安装完成有这些工具&#xff0c;后面备用&#xff1a; G:\Python38-32\Scripts\pyrcc5.exe G:\Python38-32\Scripts\pyuic5.exe 二、PyQt环境配置 pip install PyQt5 pip install pyqt5-tools 建议使用国内源&#xff0c…...

3d扫描建模产品开发-三维扫描检测蓝光检测

现当下&#xff0c;汽车制造、航空航天&#xff0c;还是消费电子、医疗器械&#xff0c;三维扫描检测与蓝光技术正以前所未有的精度和效率&#xff0c;推动着产品从概念到实物的快速转化。 三维扫描技术&#xff0c;简而言之&#xff0c;就是通过激光、结构光&#xff08;如蓝…...

多线程——01

1. 进程/任务 process/task 进程是操作系统对一个正在运行的程序的一种抽象&#xff0c;换言之&#xff0c;可以把进程看做程序的一次运行过程 每个进程想要执行&#xff0c;都需要消耗一定的系统资源&#xff08;硬件资源&#xff09; 1&#xff09;进程在系统中的作用 a…...

Makefile中-D选项定义预编译处理宏

1.简单示例假设我们有一个简单的 C 程序main.c,内容如下: #include <stdio.h> #ifdef DEBUGvoid print_debug_info() {printf("This is debug information.\n");} #elsevoid print_debug_info() {} #endif int main() {print_debug_info();printf("Prog…...

NAPI杂记

NAPI和tasklet区别&#xff1a;NAPI是利用的软中断NET_RX_SOFTIRQ实现&#xff0c; tasklet是利用TASKLET_SOFTIRQ实现 另外文章说NAPI主要是采用轮询&#xff0c;可以减少中断触发次数。do_softirq里面是开中断执行的啊&#xff1f; 中断处理函数关闭中断开始处理。如果此时…...

006 MATLAB编程基础

01 M文件 MATLAB输入命令有两种方法&#xff1a; 一是在MATLAB主窗口逐行输入命令&#xff0c;每个命令之间用分号或逗号分隔&#xff0c;每行可包含多个命令。 二是将命令组织成一个命令语句文集&#xff0c;使用扩展名“.m”&#xff0c;称为M文件。它由一系列的命令和语句…...

使用Alpine镜像作为基础镜像的Dockerfile配置

配置阿里 apk源&#xff1a; /etc/apk/repositories&#xff1a; https://mirrors.aliyun.com/alpine/v3.13/main alpine-v3.13-community安装包下载_开源镜像站-阿里云 或者使用命令 sed -i sdl-cdn.alpinelinux.orgmirrors.aliyun.comg /etc/apk/repositories dockerfil…...

RAG (Retrieval Augmented Generation) 检索增强和生成

1 RAG技术简介 1.1 RAG技术概述 RAG&#xff08;Retrieval Augmented Generation&#xff09; 是一种结合了检索&#xff08;Retrieval&#xff09;和生成&#xff08;Generation&#xff09;的技术&#xff0c;旨在通过利用外部知识库来增强大型语言模型&#xff08;LLMs&am…...

Figma入门-约束与对齐

Figma入门-约束与对齐 前言 在之前的工作中&#xff0c;大家的原型图都是使用 Axure 制作的&#xff0c;印象中 Figma 一直是个专业设计软件。 最近&#xff0c;很多产品朋友告诉我&#xff0c;很多原型图都开始用Figma制作了&#xff0c;并且很多组件都是内置的&#xff0c…...

039集——渐变色之:CAD中画彩虹()(CAD—C#二次开发入门)

&#xff08;来左边儿 跟我一起画个龙&#xff0c;在你右边儿 画一道彩虹 ~~~~~~~~~~~ &#xff09; 效果如下&#xff1a; namespace AcTools {public class Class1{public Wform.Timer timer;//定时器需建在类下面public static DateTime startTime;[CommandM…...

MTK主板_小型联发科安卓主板_行业智能终端主板基于联发科方案

MTK安卓主板是一款小巧而高效的科技产品&#xff0c;其尺寸仅为43.4mm x 57.6mm。采用了先进的联发科12nm制程工艺&#xff0c;这款主板搭载四核或八核64位A53架构的CPU&#xff0c;主频高达2.0GHz&#xff0c;不但保证了出色的计算能力&#xff0c;还实现了超低功耗的特点。系…...

Arrays.copyOfRange(),System.arraycopy() 数组复制,数组扩容

Arrays.copyOfRange() 当需要将数组中的 长度扩容时, 数组复制 使用 需要用到Arrays 类提供的的 参数解析 * param original the array from which a range is to be copied * param from the initial index of the range to be copied, inclusive * param to the final ind…...

Docker for Everyone Plus——No Enough Privilege

直接告诉我们flag在/flag中&#xff0c;访问第一小题&#xff1a; sudo -l查看允许提权执行的命令&#xff1a; 发现有image load命令 题目指明了有rz命令&#xff0c;可以用ZMODEM接收文件&#xff0c;看到一些write up说可以用XShell、MobaXterm、Tabby Terminal等软件连接上…...

ElasticSearch学习笔记把:Springboot整合ES(二)

一、前言 上一篇文章中我们学习了ES中的Term级别的查询&#xff0c;包括 term、terms、terms_set、rang等&#xff0c;今天我们使用Java代码实现一遍上述的查询。 二、项目依赖 POM <?xml version"1.0" encoding"UTF-8"?> <project xmlns&q…...

Linux系统之iotop命令的基本使用

Linux系统之iotop命令的基本使用 一、iotop命令介绍二、iotop命令的使用帮助2.1 安装iotop2.2 iotop命令help帮助信息2.3 iotop命令选项解释 三、 iotop命令的基本使用四、iotop使用注意事项 一、iotop命令介绍 iotop 是一个类似于 top 的命令行工具&#xff0c;但它专注于显示…...

根据合约地址判断合约协议的方法

判断合约协议之前&#xff0c;需要了解一下什么是ERC165协议&#xff1a; ERC165 是以太坊中用于标准化接口检测的协议&#xff0c;由 Fabian Vogelsteller 在 2018 年创建 &#xff0c;其核心内容主要包括以下方面&#xff1a; 接口定义 单一函数接口&#xff1a;ERC165 协议…...

什么是sfp,onu,​为什么PON(​俗称“光猫”​)模块使用SC光纤接口

在现代网络设备中&#xff0c;我们经常会看到SFP或SFP接口的身影&#xff0c;这些接口有时被简称为光口&#xff0c;但这个称呼并不严谨。有些厂商则称之为多功能口或多用途口&#xff0c;然而这对于不了解的人来说可能还是一头雾水。SFP&#xff0c;即Small Form-Factor Plugg…...

链表?->?(以尾插法说明,附头插法)

这篇文章做一些关于初学链表的一些问题的解答。 我知道有些朋友只是需要一份完整的关于链表的代码&#xff0c;我会附在后面&#xff0c;大家直接划到最后就好。 一、创建链表 (1 相信所有搜索过链表创建的朋友都看过这样一行&#xff1a; struct Line* head (struct Line…...

11.29周五F34-Day10打卡

文章目录 1. 问问他能不能来。解析答案:【解析答案分析】【对比分析】【拓展内容】2. 问题是他能不能做。解析答案:【解析答案分析】3. 问题是我们能否联系得上她。(什么关系?动作 or 描述?)解析答案:【解析答案分析】【对比分析】4. 我们在讨论是否要开一个会。解析答案:…...

【项目日记】仿mudou的高并发服务器 --- 实现HTTP服务器

对于生命&#xff0c;你不妨大胆一点&#xff0c; 因为我们始终要失去它。 --- 尼采 --- ✨✨✨项目地址在这里 ✨✨✨ ✨✨✨https://gitee.com/penggli_2_0/TcpServer✨✨✨ 仿mudou的高并发服务器 1 前言2 Util工具类3 HTTP协议3.1 HTTP请求3.2 HTTP应答 4 上下文解析模块…...

【SpringBoot问题】IDEA中用Service窗口展示所有服务及端口的办法

1、调出Service窗口 打开View→Tool Windows→Service&#xff0c;即可显示。 2、正常情况应该已经出现SpringBoot&#xff0c;如下图请继续第三步 3、配置Service窗口的项目启动类型。微服务一般是Springboot类型。所以这里需要选择一下。 点击最后一个号&#xff0c;点击Ru…...

Ubuntu20.04运行LARVIO

文章目录 1.运行 Toyish 示例程序2.运行 ROS Nodelet参考 1.运行 Toyish 示例程序 LARVIO 提供了一个简化的toyish示例程序&#xff0c;适合快速验证和测试。 编译项目 进入 build 文件夹并通过 CMake 编译项目&#xff1a; mkdir build cd build cmake -D CMAKE_BUILD_TYPER…...

数字化转型背景下,高职院校计算机网络应用的革新策略

在当今信息化时代&#xff0c;计算机网络已经成为高职院校教育不可或缺的一部分&#xff0c;它不仅极大地丰富了教育资源&#xff0c;提高了交流的便捷性&#xff0c;还催生了多样化的教学模式。对于高职院校来说&#xff0c;加强计算机网络应用的建设不仅是顺应时代潮流的必然…...

mysql 里面的主表和子表是什么?如何创建主表和子表的关系

在MySQL数据库中&#xff0c;主表和子表的概念是基于表间关系的。它们通常通过外键约束来建立联系&#xff0c;这种关系有助于维护数据的完整性和一致性。以下是对MySQL中主表和子表的详细解释&#xff1a; 主表&#xff08;父表&#xff09; 定义&#xff1a;主表&#xff0c…...

工程企业的成本管理系统软件应该有哪些特点?

工程企业的成本管理系统软件需要兼顾工程项目的复杂性和动态性&#xff0c;其功能特点应服务于成本核算、监控、优化与分析全生命周期管理&#xff0c;以下是关键特点&#xff1a; 一、核心功能特点 1. 全生命周期成本管理 覆盖范围&#xff1a; 从项目立项、投标、预算编制&…...

【前端开发】实战:课表安排(HTML + JavaScript + Vue3 + Vant)

后端开发 主要定义了三个核心部分&#xff1a;每周周期&#xff08;WeekDays&#xff09;、每天节次&#xff08;TimeLessons&#xff09; 和 每天节次详情&#xff08;Details&#xff09; 每周周期&#xff08;WeekDays&#xff09; 存储了每周的七天&#xff08;通常是从周一…...

nodejs建立TCP服务器端和TCP客户端之间的连接

TCP服务器端&#xff0c;看名字也知道是建立在服务器上面的 //获取模块 const net require(net); //创建server服务器 const servernet.createServer(); //与客户端建立连接 server.on(connection,function(socket){console.log(客户端与服务器连接已经建立);//socket是客户端…...

Pytorch使用手册-What is torch.nn really?(专题九)

我们建议将本教程作为 notebook 而不是脚本运行。要下载 notebook(.ipynb)文件,请点击页面顶部的链接。 PyTorch 提供了精心设计的模块和类,如 torch.nn、torch.optim、Dataset 和 DataLoader,帮助你创建和训练神经网络。为了充分利用这些工具的强大功能并根据你的问题进…...

ADAM优化算法与学习率调度器:深度学习中的关键工具

深度学习模型的训练效果离不开优化算法和学习率的选择。ADAM&#xff08;Adaptive Moment Estimation&#xff09;作为深度学习领域中广泛应用的优化算法之一&#xff0c;以其高效性和鲁棒性成为许多任务的默认选择。而学习率调度器则是优化算法的“助推器”&#xff0c;帮助训…...

黑马2024AI+JavaWeb开发入门Day03-Maven-单元测试飞书作业

视频地址&#xff1a;哔哩哔哩 讲义作业飞书地址&#xff1a;飞书 作业比较简单&#xff0c;随便写了写 package org.example;import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.ju…...

TouchGFX设计模式代码实例说明

一)Model - View - Presenter (MVP) 模式在 TouchGFX 中的应用 1)Model&#xff08;模型&#xff09;&#xff1a; 模型代表应用程序的数据和业务逻辑。例如&#xff0c;在一个简单的计数器应用中&#xff0c;模型可以是一个包含计数器当前值的类。 class CounterModel { pri…...

Docker扩容操作(docker总是空间不足)

Docker扩容操作(docker总是空间不足) 1、df二连&#xff0c;一共也就70g&#xff0c;总是占满93%以上。所以需要移动到其他目录上 查看docker镜像和容器存储目录的空间大小 du -sh /var/lib/docker/2、停止docker服务 systemctl stop docker3、首先创建目录并迁移 # 首先创…...

ELK Fleet JAVA LOG收集与展示教程

目录 elastic-fleet架构 Elastic Agent的优势 Fleet JAVA日志收集流程 1.注册Fleet Sever 2.创建JAVA代理收集策略Agent Policy 3.添加集成 integration 4.调整Java log输出格式 5.添加Elastic Agent 6.添加Ingest 7.创建数据视图 8.其他Policy nginx-policy syst…...