From cbbdc5627eadc02d4a745fea1c879fa5b6968d33 Mon Sep 17 00:00:00 2001 From: Chaos Date: Fri, 14 Nov 2025 23:22:11 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E5=AE=9E=E7=8E=B0=E5=9F=BA?= =?UTF-8?q?=E4=BA=8EJWT=E7=9A=84=E6=9D=83=E9=99=90=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E6=9C=BA=E5=88=B6-=20=E6=96=B0=E5=A2=9EHLSController=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E5=99=A8=EF=BC=8C=E6=8F=90=E4=BE=9B=E5=B8=A6=E6=9D=83?= =?UTF-8?q?=E9=99=90=E6=A0=A1=E9=AA=8C=E7=9A=84HLS=E6=8E=A5=E5=8F=A3=20-?= =?UTF-8?q?=20=E4=BF=AE=E6=94=B9JwtAuthenticationTokenFilter=EF=BC=8C?= =?UTF-8?q?=E7=A7=BB=E9=99=A4UserDetailsService=E4=BE=9D=E8=B5=96=20-?= =?UTF-8?q?=E4=BC=98=E5=8C=96JWT=E5=B7=A5=E5=85=B7=E7=B1=BB=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=9C=A8Token=E4=B8=AD=E5=AD=98=E5=82=A8?= =?UTF-8?q?=E5=92=8C=E8=A7=A3=E6=9E=90=E7=94=A8=E6=88=B7=E6=9D=83=E9=99=90?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=20-=20=E6=9B=B4=E6=96=B0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E6=9C=8D=E5=8A=A1=E5=AE=9E=E7=8E=B0=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E6=9D=83=E9=99=90=E6=9F=A5=E8=AF=A2=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=AE=B0=E5=BD=95=20-=20=E8=B0=83=E6=95=B4Token?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E9=80=BB=E8=BE=91=EF=BC=8C=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E9=80=9A=E8=BF=87JWT=E8=A7=A3=E7=A0=81=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E6=9C=89=E6=95=88=E6=80=A7=E5=88=A4=E6=96=AD=20-=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=9D=83=E9=99=90=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=88=B0?= =?UTF-8?q?SimpleGrantedAuthority=E5=AF=B9=E8=B1=A1=E7=9A=84=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jwt/JwtAuthenticationTokenFilter.java | 18 +++++--- .../chaos_api/controller/HLSController.java | 20 +++++++++ .../service/impl/UserDetailsServiceImpl.java | 3 ++ .../cn/nopj/chaos_api/util/JwtTokenUtil.java | 45 +++++++++++++++++-- 4 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/HLSController.java diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/config/jwt/JwtAuthenticationTokenFilter.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/config/jwt/JwtAuthenticationTokenFilter.java index 17f98fb..363bcbe 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/config/jwt/JwtAuthenticationTokenFilter.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/config/jwt/JwtAuthenticationTokenFilter.java @@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -17,6 +18,7 @@ import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; +import java.util.List; /** * JWT 登录授权过滤器 @@ -25,8 +27,6 @@ import java.io.IOException; @Slf4j public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { - @Autowired - private UserDetailsService userDetailsService; @Autowired private JwtTokenUtil jwtTokenUtil; @Value("${jwt.tokenHeader}") @@ -49,11 +49,19 @@ public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { log.info("username={}", username); // 如果 Token 中有用户名但上下文中没有,说明是首次登录 if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { - UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); + // 验证 Token 是否有效 - if (jwtTokenUtil.validateToken(authToken, userDetails)) { + if (jwtTokenUtil.validateToken(authToken)) { + + List authorityStrings = jwtTokenUtil.getAuthoritiesFromToken(authToken); + + List authorities = authorityStrings.stream() + .map(SimpleGrantedAuthority::new) + .toList(); + log.info("authorities: {}", authorities); UsernamePasswordAuthenticationToken authentication = - new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); + new UsernamePasswordAuthenticationToken(username, null, authorities); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/HLSController.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/HLSController.java new file mode 100644 index 0000000..8515fd9 --- /dev/null +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/controller/HLSController.java @@ -0,0 +1,20 @@ +package cn.nopj.chaos_api.controller; + +import cn.nopj.chaos_api.model.ApiResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequestMapping("/api/hls") +public class HLSController { + + @PreAuthorize("hasAuthority('admin')") + @GetMapping("/") + ApiResult getHLS(){ + return ApiResult.success("HLS is radar"); + } +} \ No newline at end of file diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/UserDetailsServiceImpl.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/UserDetailsServiceImpl.java index b5d31dc..aba8d0d 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/UserDetailsServiceImpl.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/service/impl/UserDetailsServiceImpl.java @@ -3,6 +3,7 @@ package cn.nopj.chaos_api.service.impl; import cn.nopj.chaos_api.domain.entity.User; import cn.nopj.chaos_api.mapper.UserMapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -15,6 +16,7 @@ import java.util.List; import java.util.stream.Collectors; @Service +@Slf4j public class UserDetailsServiceImpl implements UserDetailsService { @Autowired @@ -30,6 +32,7 @@ public class UserDetailsServiceImpl implements UserDetailsService { // 2. 查询该用户的权限信息 (角色 + 权限) List authorities = userMapper.findAuthoritiesByUsername(username); + log.info("用户权限列表: {}", authorities); // 3. 将权限字符串列表转换为 GrantedAuthority 集合 List grantedAuthorities = authorities.stream() .map(SimpleGrantedAuthority::new) diff --git a/chaos_api_web/src/main/java/cn/nopj/chaos_api/util/JwtTokenUtil.java b/chaos_api_web/src/main/java/cn/nopj/chaos_api/util/JwtTokenUtil.java index 416935c..6f89c3d 100644 --- a/chaos_api_web/src/main/java/cn/nopj/chaos_api/util/JwtTokenUtil.java +++ b/chaos_api_web/src/main/java/cn/nopj/chaos_api/util/JwtTokenUtil.java @@ -6,12 +6,15 @@ import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.interfaces.DecodedJWT; import jakarta.annotation.PostConstruct; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.userdetails.UserDetails; import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; @Slf4j @@ -53,10 +56,9 @@ public class JwtTokenUtil { /** * 验证 Token 是否有效 */ - public boolean validateToken(String token, UserDetails userDetails) { + public boolean validateToken(String token) { try { - String username = getUsernameFromToken(token); - return username != null && username.equals(userDetails.getUsername()) && !isTokenExpired(token); + return decodeToken(token) != null; } catch (Exception e) { return false; } @@ -69,10 +71,17 @@ public class JwtTokenUtil { Date now = new Date(); Date expiryDate = new Date(now.getTime() + expiration * 1000); + List authorities = userDetails.getAuthorities() + .stream() + .map(GrantedAuthority::getAuthority) + .toList(); + + return JWT.create() .withSubject(userDetails.getUsername()) .withIssuedAt(now) .withExpiresAt(expiryDate) + .withClaim("authorities", authorities) .sign(algorithm); } @@ -86,4 +95,34 @@ public class JwtTokenUtil { } } + + /** + * 解析 Token + * @param token Token + * @return 解析后的 Token + */ + private DecodedJWT decodeToken(String token) { + try { + return JWT.decode(token); + } catch (Exception e) { + return null; + } + } + + + /** + * 从 Token 中获取权限列表 + * @param token Token + * @return 权限列表 + */ + public List getAuthoritiesFromToken(String token){ + DecodedJWT decodedJWT = decodeToken(token); + if (decodedJWT == null){ + return null; + } + return decodedJWT.getClaim("authorities") + .asList(String.class); + + } + }