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

Springboot3.X+security6.5+jdk21

1.引入依赖

		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId><version>3.5.7</version></dependency><!-- JWT (兼容 JDK 21) --><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.12.3</version></dependency>

2.yml配置

app:jwt:secret: "mySuperSecretKeyForJWTGenerationInSpringBoot3WithJDK21"expiration: 86400000 # 24小时refresh-expiration: 604800000 # 7

3.代码

3.1 GrantedAuthority

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
@Data
@JsonSerialize
@NoArgsConstructor
@AllArgsConstructor
public class CustomGrantedAuthority implements GrantedAuthority {private String authority;@Overridepublic String getAuthority() {return this.authority;}
}

3.2 UserDetails实现

import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@Data
public class MyUserDetails implements UserDetails {private String id;private String userName;private String password;private String role;private Collection<CustomGrantedAuthority> authorities;public MyUserDetails(User user) {this.id = user.getId();this.userName = user.getAccountName();this.password = user.getPassword();}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return this.authorities;}@Overridepublic String getPassword() {return this.password;}@Overridepublic String getUsername() {return this.userName;}
}

3.3 UserDetailsService实现

import lombok.RequiredArgsConstructor;
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;@Service
@RequiredArgsConstructor
public class MyUserService implements UserDetailsService {private final IUserService iUserService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User one = iUserService.lambdaQuery().eq(User::getAccountName, username).one();if (one == null) {throw new UsernameNotFoundException("用户不存在: " + username);}one.setPassword(new BCryptPasswordEncoder().encode(one.getPassword()));MyUserDetails myUserDetails = new MyUserDetails(one);//角色List<CustomGrantedAuthority> authorities = new ArrayList<>();authorities.add(new CustomGrantedAuthority("TEST"));//权限--多个authorities.add(new CustomGrantedAuthority("menu:ecu:list"));myUserDetails.setAuthorities(authorities);return myUserDetails;}
}

3.4 JwtTokenUtil

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import javax.crypto.SecretKey;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;@Component
public class JwtTokenUtil {@Value("${app.jwt.secret}")private String secret;@Value("${app.jwt.expiration}")private Long expiration;@Value("${app.jwt.refresh-expiration}")private Long refreshExpiration;private SecretKey getSigningKey() {return Keys.hmacShaKeyFor(secret.getBytes());}public String extractUsername(String token) {return extractClaim(token, Claims::getSubject);}public Date extractExpiration(String token) {return extractClaim(token, Claims::getExpiration);}public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {final Claims claims = extractAllClaims(token);return claimsResolver.apply(claims);}private Claims extractAllClaims(String token) {return Jwts.parser().verifyWith(getSigningKey()).build().parseSignedClaims(token).getPayload();}private Boolean isTokenExpired(String token) {return extractExpiration(token).before(new Date());}public String generateToken(UserDetails userDetails) {Map<String, Object> claims = new HashMap<>();// 可以在这里添加额外信息到 token 中if (userDetails instanceof MyUserDetails myUserDetails) {claims.put("userId", myUserDetails.getId());claims.put("role", myUserDetails.getRole());claims.put("authorities",userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));}return createToken(claims, userDetails.getUsername(), expiration);}public String generateRefreshToken(UserDetails userDetails) {Map<String, Object> claims = new HashMap<>();return createToken(claims, userDetails.getUsername(), refreshExpiration);}private String createToken(Map<String, Object> claims, String subject, Long expiration) {return Jwts.builder().claims(claims).subject(subject).issuedAt(new Date(System.currentTimeMillis())).expiration(new Date(System.currentTimeMillis() + expiration)).signWith(getSigningKey()).compact();}public Boolean validateToken(String token, UserDetails userDetails) {final String username = extractUsername(token);return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));}// 新增:从 token 中提取用户IDpublic String extractUserId(String token) {final Claims claims = extractAllClaims(token);return claims.get("userId", String.class);}// 新增:从 token 中提取角色public String extractRole(String token) {final Claims claims = extractAllClaims(token);return claims.get("role", String.class);}public Authentication getAuthentication(String token) {Claims claims = extractAllClaims(token);Collection<? extends GrantedAuthority> authorities =Arrays.stream(claims.get("authorities").toString().split(",")).map(CustomGrantedAuthority::new).collect(Collectors.toList());UserDetails userDetails = new org.springframework.security.core.userdetails.User(claims.getSubject(), "", authorities);return new UsernamePasswordAuthenticationToken(userDetails, "", authorities);}
}

3.5 OncePerRequestFilter

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {private final JwtTokenUtil jwtTokenUtil;private final MyUserService userService;@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws ServletException, IOException {final String requestTokenHeader = request.getHeader("Authorization");String username = null;String jwtToken = null;if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {jwtToken = requestTokenHeader.substring(7);try {username = jwtTokenUtil.extractUsername(jwtToken);} catch (Exception e) {logger.warn("JWT Token 解析失败: " + e.getMessage());}}if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {MyUserDetails userDetails = (MyUserDetails) this.userService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {UsernamePasswordAuthenticationToken authToken =new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authToken);}}chain.doFilter(request, response);}
}

3.6 SecurityConfig

import lombok.RequiredArgsConstructor;
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.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
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.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.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {private final UserDetailsService userDetailsService;private final JwtAuthenticationFilter jwtAuthenticationFilter;@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}public static void main(String[] args) {BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();String encode = bCryptPasswordEncoder.encode("123456");System.out.println(bCryptPasswordEncoder.matches("123456", encode));}/*** Spring Security 6 推荐的方式* 框架会自动配置 DaoAuthenticationProvider*/@Beanpublic AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {return config.getAuthenticationManager();}@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf(AbstractHttpConfigurer::disable).sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)).authorizeHttpRequests(auth -> auth.requestMatchers("/api/auth/**","/v3/api-docs/**","/swagger-ui/**","/swagger-ui.html").permitAll().requestMatchers("/api/admin/**").hasRole("ADMIN").requestMatchers("/api/user/**").hasAnyRole("USER", "ADMIN").anyRequest().authenticated()).userDetailsService(userDetailsService) // 设置 UserDetailsService.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);return http.build();}
}

4. 登录验证

4.1 DTO

@Data
public class LoginRequest {@NotBlank(message = "用户名不能为空")@Size(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间")private String username;@NotBlank(message = "密码不能为空")@Size(min = 6, max = 100, message = "密码长度必须在6-100个字符之间")private String password;
}
@Data
public class RegisterRequest {@NotBlank(message = "用户名不能为空")@Size(min = 3, max = 50, message = "用户名长度必须在3-50个字符之间")private String username;@NotBlank(message = "邮箱不能为空")@Email(message = "邮箱格式不正确")private String email;@NotBlank(message = "密码不能为空")@Size(min = 6, max = 100, message = "密码长度必须在6-100个字符之间")private String password;// 可选:确认密码字段private String confirmPassword;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JwtResponse {private String token;private String refreshToken;private String type = "Bearer";private String username;private String email;private String role;private Long userId;private List<String> authorities;public JwtResponse(String token, String refreshToken, String username, String role,List<String> authorities) {this.token = token;this.refreshToken = refreshToken;this.username = username;this.role = role;this.authorities = authorities;}public JwtResponse(String token, String refreshToken, String username, String email, String role, Long userId) {this.token = token;this.refreshToken = refreshToken;this.username = username;this.email = email;this.role = role;this.userId = userId;}
}

4.2 controller

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
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.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;import java.util.stream.Collectors;@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {private final AuthenticationManager authenticationManager;private final MyUserService userService;private final JwtTokenUtil jwtTokenUtil;@PostMapping("/login")public RespResult<?> login(@Valid @RequestBody LoginRequest request) {Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));SecurityContextHolder.getContext().setAuthentication(authentication);MyUserDetails userDetails = (MyUserDetails) authentication.getPrincipal();String token = jwtTokenUtil.generateToken(userDetails);String refreshToken = jwtTokenUtil.generateRefreshToken(userDetails);return RespResult.success(new JwtResponse(token, refreshToken,userDetails.getUsername(),userDetails.getRole(),userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList())));}
/*	@PostMapping("/register")public ResponseEntity<?> register(@Valid @RequestBody RegisterRequest request) {User user = new User();user.setUsername(request.getUsername());user.setEmail(request.getEmail());user.setPassword(request.getPassword());User savedUser = userService.register(user);return ResponseEntity.ok("用户注册成功,ID: " + savedUser.getId());}*/@PostMapping("/refresh")public ResponseEntity<?> refreshToken(@RequestHeader("Authorization") String refreshToken) {if (refreshToken.startsWith("Bearer ")) {refreshToken = refreshToken.substring(7);}String username = jwtTokenUtil.extractUsername(refreshToken);MyUserDetails userDetails = (MyUserDetails) userService.loadUserByUsername(username);if (jwtTokenUtil.validateToken(refreshToken, userDetails)) {String newToken = jwtTokenUtil.generateToken(userDetails);return ResponseEntity.ok(new JwtResponse(newToken, refreshToken, username, userDetails.getRole(),userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList())));}return ResponseEntity.badRequest().body("Refresh Token 无效");}}

4.3 Postman

在这里插入图片描述

http://www.dtcms.com/a/596415.html

相关文章:

  • 中文企业网站设计欣赏宁波网站建设方案联系方式
  • Vue2 入门到实战(day2):计算属性、监视属性、样式绑定与条件渲染(附代码案例)
  • C语言位运算深度应用:嵌入式硬件寄存器控制与低功耗优化实践
  • 深圳建立网站营销用动易建设网站教程
  • 京东后端架构技术,Pipline 设计 解决复杂查询逻辑
  • 《STM32 江湖 SPI 双绝:硬件外设与软件模拟的深度解析》
  • Docker学习笔记---day002
  • F280049C学习笔记之X-BAR
  • Python基础教学:Python的openpyxl和python-docx模块结合Excel和Word模板进行数据写入-由Deepseek产生
  • WebSocket原理及实现详解
  • 网站建设与管理吴振峰pptapp的制作需要多少钱
  • 优雅与极简:将你的屏幕变成复古翻页钟——Fliqlo for Mac 完全指南
  • wsl ubuntu24.04 cuda13 cudnn9 pytorch 显卡加速
  • macos安装mysql
  • 解决 iPhone 和 Mac 之间备忘录无法同步的9种方法
  • 【Ubuntu系统开机后出现:GNU GRUB ,Advanced options for Ubuntu】
  • 江西省建设监督网站电子网特色的企业网站建设
  • Mac上DevEco-Studio功能/使用介绍
  • Redis 配置详解
  • Mac 下载 VMware 11.1.0-1.dmg 后如何安装?超简单教程
  • mac怎么卸载office Powerpoint
  • dz论坛做分类网站wordpress git 7.5
  • Java 大文件上传实战:从底层原理到分布式落地(含分片 / 断点续传 / 秒传)
  • 有趣的网站网址之家wordpress网站中英文切换
  • 「腾讯云NoSQL」技术之Redis篇:精准围剿rehash时延毛刺实践方案揭秘
  • 中控播控系统:一键掌控多媒体空间
  • 遗传算法与粒子群算法优化BP提高分类效果
  • c++ -- 循环依赖解决方案
  • 免费vip网站推广做疏通什么网站推广好
  • 金融智能体具体能做什么?应用场景有哪些?