From 1bb9a251a7f96e9d3436502466250e0e4ad99d57 Mon Sep 17 00:00:00 2001 From: Bunny Date: Fri, 18 Jul 2025 17:15:00 +0800 Subject: [PATCH] =?UTF-8?q?:fire:=20=E4=BF=AE=E6=94=B9=E4=B8=8D=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PermissionController.java | 6 + .../step3/controller/RoleController.java | 4 + .../controller/RolePermissionController.java | 7 + .../step3/controller/UserController.java | 5 + .../step3/controller/UserRoleController.java | 7 + .../SecurityPreAuthorizationController.java | 58 +++++++ .../AuthorizationManagerConfiguration.java | 21 --- .../config/SecurityWebConfiguration.java | 4 +- .../filter/JwtAuthenticationFilter.java | 149 ++++++++++-------- .../AuthorizationManagerConfiguration.java | 28 ++++ .../manger/PostAuthorizationManager.java | 123 ++++++++++----- .../manger/PreAuthorizationManager.java | 116 +++++++++----- .../static/src/config/axios-config.js | 2 +- 13 files changed, 356 insertions(+), 174 deletions(-) create mode 100644 spring-security/step-3/src/main/java/com/spring/step3/controller/test/SecurityPreAuthorizationController.java delete mode 100644 spring-security/step-3/src/main/java/com/spring/step3/security/config/AuthorizationManagerConfiguration.java create mode 100644 spring-security/step-3/src/main/java/com/spring/step3/security/manger/AuthorizationManagerConfiguration.java diff --git a/spring-security/step-3/src/main/java/com/spring/step3/controller/PermissionController.java b/spring-security/step-3/src/main/java/com/spring/step3/controller/PermissionController.java index 8e47611..36f8b2f 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/controller/PermissionController.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/controller/PermissionController.java @@ -12,6 +12,7 @@ import com.spring.step3.service.roles.PermissionService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -34,6 +35,7 @@ public class PermissionController { private final PermissionService permissionService; + @PermitAll @Operation(summary = "分页查询系统权限表", description = "分页系统权限表") @GetMapping("{page}/{limit}") public Result> getPermissionPage( @@ -47,6 +49,7 @@ public class PermissionController { return Result.success(pageResult); } + @PermitAll @Operation(summary = "所有的权限列表", description = "获取所有的权限列表") @GetMapping("all") public Result> getAllPermission() { @@ -54,6 +57,7 @@ public class PermissionController { return Result.success(voList); } + @PermitAll @Operation(summary = "添加系统权限表", description = "添加系统权限表") @PostMapping() public Result addPermission(@Valid @RequestBody PermissionDto dto) { @@ -61,6 +65,7 @@ public class PermissionController { return Result.success(ResultCodeEnum.ADD_SUCCESS); } + @PermitAll @Operation(summary = "更新系统权限表", description = "更新系统权限表") @PutMapping() public Result updatePermission(@Valid @RequestBody PermissionDto dto) { @@ -68,6 +73,7 @@ public class PermissionController { return Result.success(ResultCodeEnum.UPDATE_SUCCESS); } + @PermitAll @Operation(summary = "删除系统权限表", description = "删除系统权限表") @DeleteMapping() public Result deletePermission(@RequestBody List ids) { diff --git a/spring-security/step-3/src/main/java/com/spring/step3/controller/RoleController.java b/spring-security/step-3/src/main/java/com/spring/step3/controller/RoleController.java index 46218c9..08ad618 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/controller/RoleController.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/controller/RoleController.java @@ -35,6 +35,7 @@ public class RoleController { private final RoleService roleService; + @PermitAll @Operation(summary = "分页查询系统角色表", description = "分页系统角色表") @GetMapping("{page}/{limit}") public Result> getRolePage( @@ -56,6 +57,7 @@ public class RoleController { return Result.success(roleVoList); } + @PermitAll @Operation(summary = "添加系统角色表", description = "添加系统角色表") @PostMapping() public Result addRole(@Valid @RequestBody RoleDto dto) { @@ -63,6 +65,7 @@ public class RoleController { return Result.success(ResultCodeEnum.ADD_SUCCESS); } + @PermitAll @Operation(summary = "更新系统角色表", description = "更新系统角色表") @PutMapping() public Result updateRole(@Valid @RequestBody RoleDto dto) { @@ -70,6 +73,7 @@ public class RoleController { return Result.success(ResultCodeEnum.UPDATE_SUCCESS); } + @PermitAll @Operation(summary = "删除系统角色表", description = "删除系统角色表") @DeleteMapping() public Result deleteRole(@RequestBody List ids) { diff --git a/spring-security/step-3/src/main/java/com/spring/step3/controller/RolePermissionController.java b/spring-security/step-3/src/main/java/com/spring/step3/controller/RolePermissionController.java index 12544f9..7c3cf04 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/controller/RolePermissionController.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/controller/RolePermissionController.java @@ -13,6 +13,7 @@ import com.spring.step3.service.roles.RolePermissionService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -35,6 +36,7 @@ public class RolePermissionController { private final RolePermissionService rolePermissionService; + @PermitAll @Operation(summary = "分页查询角色权限关联表", description = "分页角色权限关联表") @GetMapping("{page}/{limit}") public Result> getRolePermissionPage( @@ -49,12 +51,14 @@ public class RolePermissionController { } @GetMapping("permissions") + @PermitAll @Operation(summary = "根据角色id获取权限内容", description = "根据角色id获取权限内容") public Result> getRolePermissionById(Long permissionId) { List voList = rolePermissionService.getRolePermissionById(permissionId); return Result.success(voList); } + @PermitAll @Operation(summary = "添加角色权限关联表", description = "添加角色权限关联表") @PostMapping() public Result addRolePermission(@Valid @RequestBody RolePermissionDto dto) { @@ -62,6 +66,7 @@ public class RolePermissionController { return Result.success(ResultCodeEnum.ADD_SUCCESS); } + @PermitAll @Operation(summary = "为角色分配权限", description = "根据角色id分配权限") @PostMapping("assign-permission") public Result assignRolePermission(@Valid @RequestBody AssignRolePermissionDto dto) { @@ -69,6 +74,7 @@ public class RolePermissionController { return Result.success(); } + @PermitAll @Operation(summary = "更新角色权限关联表", description = "更新角色权限关联表") @PutMapping() public Result updateRolePermission(@Valid @RequestBody RolePermissionDto dto) { @@ -76,6 +82,7 @@ public class RolePermissionController { return Result.success(ResultCodeEnum.UPDATE_SUCCESS); } + @PermitAll @Operation(summary = "删除角色权限关联表", description = "删除角色权限关联表") @DeleteMapping() public Result deleteRolePermission(@RequestBody List ids) { diff --git a/spring-security/step-3/src/main/java/com/spring/step3/controller/UserController.java b/spring-security/step-3/src/main/java/com/spring/step3/controller/UserController.java index f606a16..673a76e 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/controller/UserController.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/controller/UserController.java @@ -11,6 +11,7 @@ import com.spring.step3.service.user.UserService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -33,6 +34,7 @@ public class UserController { private final UserService userService; + @PermitAll @Operation(summary = "分页查询用户基本信息表", description = "分页用户基本信息表") @GetMapping("{page}/{limit}") public Result> getUserPage( @@ -46,6 +48,7 @@ public class UserController { return Result.success(pageResult, ResultCodeEnum.LOAD_FINISHED); } + @PermitAll @Operation(summary = "添加用户基本信息表", description = "添加用户基本信息表") @PostMapping() public Result addUser(@Valid @RequestBody UserDto dto) { @@ -53,6 +56,7 @@ public class UserController { return Result.success(ResultCodeEnum.ADD_SUCCESS); } + @PermitAll @Operation(summary = "更新用户基本信息表", description = "更新用户基本信息表") @PutMapping() public Result updateUser(@Valid @RequestBody UserDto dto) { @@ -60,6 +64,7 @@ public class UserController { return Result.success(ResultCodeEnum.UPDATE_SUCCESS); } + @PermitAll @Operation(summary = "删除用户基本信息表", description = "删除用户基本信息表") @DeleteMapping() public Result deleteUser(@RequestBody List ids) { diff --git a/spring-security/step-3/src/main/java/com/spring/step3/controller/UserRoleController.java b/spring-security/step-3/src/main/java/com/spring/step3/controller/UserRoleController.java index dff9e71..e7e8582 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/controller/UserRoleController.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/controller/UserRoleController.java @@ -12,6 +12,7 @@ import com.spring.step3.service.user.UserRoleService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.security.PermitAll; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -34,6 +35,7 @@ public class UserRoleController { private final UserRoleService userRoleService; + @PermitAll @Operation(summary = "分页查询用户角色关联表", description = "分页用户角色关联表") @GetMapping("{page}/{limit}") public Result> getUserRolePage( @@ -47,6 +49,7 @@ public class UserRoleController { return Result.success(pageResult); } + @PermitAll @Operation(summary = "根据用户id获取当前用户角色列表", description = "根据用户id获取当前用户角色列表") @GetMapping("roles") public Result> getRoleListByUserId(Long userId) { @@ -54,6 +57,7 @@ public class UserRoleController { return Result.success(voList); } + @PermitAll @Operation(summary = "添加用户角色关联表", description = "添加用户角色关联表") @PostMapping() public Result addUserRole(@Valid @RequestBody UserRoleDto dto) { @@ -61,6 +65,7 @@ public class UserRoleController { return Result.success(ResultCodeEnum.ADD_SUCCESS); } + @PermitAll @Operation(summary = "为用户分配角色id", description = "根据用户id分配用户角色") @PostMapping("assign-role") public Result assignUserRole(@Valid @RequestBody AssignUserRoleDto dto) { @@ -68,6 +73,7 @@ public class UserRoleController { return Result.success(); } + @PermitAll @Operation(summary = "更新用户角色关联表", description = "更新用户角色关联表") @PutMapping() public Result updateUserRole(@Valid @RequestBody UserRoleDto dto) { @@ -75,6 +81,7 @@ public class UserRoleController { return Result.success(ResultCodeEnum.UPDATE_SUCCESS); } + @PermitAll @Operation(summary = "删除用户角色关联表", description = "删除用户角色关联表") @DeleteMapping() public Result deleteUserRole(@RequestBody List ids) { diff --git a/spring-security/step-3/src/main/java/com/spring/step3/controller/test/SecurityPreAuthorizationController.java b/spring-security/step-3/src/main/java/com/spring/step3/controller/test/SecurityPreAuthorizationController.java new file mode 100644 index 0000000..030f891 --- /dev/null +++ b/spring-security/step-3/src/main/java/com/spring/step3/controller/test/SecurityPreAuthorizationController.java @@ -0,0 +1,58 @@ +package com.spring.step3.controller.test; + +import com.spring.step3.domain.vo.result.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +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; + +@Tag(name = "自定义方法前的校验", description = "自定义方法前的校验 SecurityPreAuthorization") +@Slf4j +@RestController +@RequestMapping("/api/security/pre") +public class SecurityPreAuthorizationController { + + @PreAuthorize("hasAuthority('role::read')") + @Operation(summary = "拥有 role:read 的角色可以访问", description = "当前用户拥有 role:read 角色可以访问这个接口") + @GetMapping("role-user") + public Result roleUser() { + return Result.success(); + } + + @PreAuthorize("hasAuthority('USER')") + @Operation(summary = "拥有 USER 的角色可以访问", description = "当前用户拥有 USER 角色可以访问这个接口") + @GetMapping("upper-user") + public Result upperUser() { + String data = "是区分大小写的"; + return Result.success(data); + } + + @PreAuthorize("hasAuthority('admin')") + @Operation(summary = "拥有 admin 的角色可以访问", description = "当前用户拥有 admin 角色可以访问这个接口") + @GetMapping("lower-admin") + public Result lowerAdmin() { + String data = "如果是大写,但是在这里是小写无法访问"; + return Result.success(data); + } + + @PreAuthorize("hasAuthority('ADMIN')") + @Operation(summary = "拥有 ADMIN 的角色可以访问", description = "当前用户拥有 ADMIN 角色可以访问这个接口") + @GetMapping("upper-admin") + public Result upperAdmin() { + String data = "如果是大写,但是在这里是小写无法访问"; + return Result.success(data); + } + + // @PostAuthorize("returnObject.data == authentication.name") + // @Operation(summary = "测试使用返回参数判断权限", description = "测试使用返回参数判断权限 用户拥有 role::read 可以访问这个接口") + // @GetMapping("test-post-authorize") + // public Result testPostAuthorize() { + // log.info("方法内容已经执行。。。"); + // String data = "Bunny"; + // return Result.success(data); + // } + +} diff --git a/spring-security/step-3/src/main/java/com/spring/step3/security/config/AuthorizationManagerConfiguration.java b/spring-security/step-3/src/main/java/com/spring/step3/security/config/AuthorizationManagerConfiguration.java deleted file mode 100644 index dac1428..0000000 --- a/spring-security/step-3/src/main/java/com/spring/step3/security/config/AuthorizationManagerConfiguration.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.spring.step3.security.config; - -import org.springframework.context.annotation.Configuration; - -@Configuration -// @EnableMethodSecurity(prePostEnabled = false) -public class AuthorizationManagerConfiguration { - - // @Bean - // @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - // Advisor preAuthorize(PreAuthorizationManager manager) { - // return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager); - // } - // - // @Bean - // @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - // Advisor postAuthorize(PostAuthorizationManager manager) { - // return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager); - // } - -} diff --git a/spring-security/step-3/src/main/java/com/spring/step3/security/config/SecurityWebConfiguration.java b/spring-security/step-3/src/main/java/com/spring/step3/security/config/SecurityWebConfiguration.java index 8c20035..93548ea 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/security/config/SecurityWebConfiguration.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/security/config/SecurityWebConfiguration.java @@ -18,7 +18,7 @@ import java.util.List; @Configuration @EnableWebSecurity -@EnableMethodSecurity(jsr250Enabled = true) +@EnableMethodSecurity(jsr250Enabled = true, securedEnabled = true) @RequiredArgsConstructor public class SecurityWebConfiguration { @@ -56,7 +56,7 @@ public class SecurityWebConfiguration { // 但是在 Spring过滤器中,如果要放行不需要认证请求,但是需要认证的接口必需要携带token。 // 做法是在这里定义要认证的接口,如果要做成动态可以放到数据库。 // ======================================================================= - .requestMatchers(securedPaths.toArray(String[]::new)).authenticated() + // .requestMatchers(securedPaths.toArray(String[]::new)).authenticated() // 其余请求都放行 .anyRequest().permitAll() ) diff --git a/spring-security/step-3/src/main/java/com/spring/step3/security/filter/JwtAuthenticationFilter.java b/spring-security/step-3/src/main/java/com/spring/step3/security/filter/JwtAuthenticationFilter.java index 27618f5..0c2d3b7 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/security/filter/JwtAuthenticationFilter.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/security/filter/JwtAuthenticationFilter.java @@ -39,84 +39,95 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { @Override protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, - @NotNull FilterChain filterChain) throws ServletException, IOException, AuthenticSecurityException { - // 先校验不需要认证的接口 - RequestMatcher[] requestNoAuthMatchers = SecurityWebConfiguration.noAuthPaths.stream() - .map(AntPathRequestMatcher::new) - .toArray(RequestMatcher[]::new); - OrRequestMatcher noAuthRequestMatcher = new OrRequestMatcher(requestNoAuthMatchers); - if (noAuthRequestMatcher.matches(request)) { - filterChain.doFilter(request, response); - return; - } - - // 获取需要认证的接口 - RequestMatcher[] requestSecureMatchers = SecurityWebConfiguration.securedPaths.stream() - .map(AntPathRequestMatcher::new) - .toArray(RequestMatcher[]::new); - OrRequestMatcher secureRequestMatcher = new OrRequestMatcher(requestSecureMatchers); - - // 公开接口直接放行 - if (!secureRequestMatcher.matches(request)) { - filterChain.doFilter(request, response); - return; - } - - final String authHeader = request.getHeader("Authorization"); - // 如果当前请求不包含验证Token直接返回 - if (authHeader == null || !authHeader.startsWith("Bearer ")) { - filterChain.doFilter(request, response); - throw new AuthenticSecurityException(ResultCodeEnum.LOGIN_AUTH); - } - - // 当前请求的Token - final String jwtToken = authHeader.substring(7); - + @NotNull FilterChain filterChain) + throws ServletException, IOException { try { - // 检查当前Token是否过期 - if (jwtTokenService.isExpired(jwtToken)) { - // 💡如果过期不需要进行判断和验证,需要直接放行可以像下面这样写 - // =================================================== - // filterChain.doFilter(request, response); - // return; - // =================================================== - throw new AuthenticSecurityException(ResultCodeEnum.AUTHENTICATION_EXPIRED); + // 检查白名单路径 + if (isNoAuthPath(request)) { + filterChain.doFilter(request, response); + return; } - // 解析当前Token中的用户名 - String username = jwtTokenService.getUsernameFromToken(jwtToken); - Long userId = jwtTokenService.getUserIdFromToken(jwtToken); - - // 当前用户名存在,并且 Security上下文为空,设置认证相关信息 - if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { - // 调用用户信息进行登录 - UserDetails userDetails = userDetailsService.loadUserByUsername(username); - UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( - userDetails, - null, - userDetails.getAuthorities() - ); - authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - - // 设置认证用户信息 - SecurityContextHolder.getContext().setAuthentication(authToken); - BaseContext.setUsername(username); - BaseContext.setUserId(userId); + // 检查是否需要认证的路径 + if (!isSecurePath(request)) { + filterChain.doFilter(request, response); + return; } + // 验证Token + validateAndSetAuthentication(request, response, filterChain); + filterChain.doFilter(request, response); - } - // ⚠️IMPORTANT: - // ========================================================================== - // 在 catch 块中,securityAuthenticationEntryPoint.commence() 已经处理了错误响应 - // 所以应该 直接返回,避免继续执行后续逻辑。 - // ========================================================================== - catch (RuntimeException exception) { + } catch (AuthenticSecurityException e) { + // 直接处理认证异常,不再调用filterChain.doFilter() securityAuthenticationEntryPoint.commence( request, response, - new MyAuthenticationException(exception.getMessage(), exception) + new MyAuthenticationException(e.getMessage(), e) + ); + } catch (RuntimeException e) { + securityAuthenticationEntryPoint.commence( + request, + response, + new MyAuthenticationException("Authentication failed", e) ); } } -} + + /** + * 是否是不用验证的路径 + */ + private boolean isNoAuthPath(HttpServletRequest request) { + RequestMatcher[] matchers = SecurityWebConfiguration.noAuthPaths.stream() + .map(AntPathRequestMatcher::new) + .toArray(RequestMatcher[]::new); + return new OrRequestMatcher(matchers).matches(request); + } + + /** + * 是否是要验证的路径 + */ + private boolean isSecurePath(HttpServletRequest request) { + RequestMatcher[] matchers = SecurityWebConfiguration.securedPaths.stream() + .map(AntPathRequestMatcher::new) + .toArray(RequestMatcher[]::new); + return new OrRequestMatcher(matchers).matches(request); + } + + /** + * 验证并设置身份验证 + */ + private void validateAndSetAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String authHeader = request.getHeader("Authorization"); + + // Token验证 + if (authHeader == null || !authHeader.startsWith("Bearer ")) { + filterChain.doFilter(request, response); + return; + // throw new AuthenticSecurityException(ResultCodeEnum.LOGIN_AUTH); + } + + String jwtToken = authHeader.substring(7); + + if (jwtTokenService.isExpired(jwtToken)) { + throw new AuthenticSecurityException(ResultCodeEnum.AUTHENTICATION_EXPIRED); + } + + // 设置认证信息 + String username = jwtTokenService.getUsernameFromToken(jwtToken); + Long userId = jwtTokenService.getUserIdFromToken(jwtToken); + + if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( + userDetails, + null, + userDetails.getAuthorities() + ); + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authToken); + BaseContext.setUsername(username); + BaseContext.setUserId(userId); + } + } +} \ No newline at end of file diff --git a/spring-security/step-3/src/main/java/com/spring/step3/security/manger/AuthorizationManagerConfiguration.java b/spring-security/step-3/src/main/java/com/spring/step3/security/manger/AuthorizationManagerConfiguration.java new file mode 100644 index 0000000..e0dc9eb --- /dev/null +++ b/spring-security/step-3/src/main/java/com/spring/step3/security/manger/AuthorizationManagerConfiguration.java @@ -0,0 +1,28 @@ +package com.spring.step3.security.manger; + +import org.springframework.aop.Advisor; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Role; +import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor; +import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; + +@Configuration +@EnableMethodSecurity(prePostEnabled = false) +public class AuthorizationManagerConfiguration { + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + Advisor preAuthorize(PreAuthorizationManager manager) { + return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager); + } + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + Advisor postAuthorize(PostAuthorizationManager manager) { + return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager); + } + +} diff --git a/spring-security/step-3/src/main/java/com/spring/step3/security/manger/PostAuthorizationManager.java b/spring-security/step-3/src/main/java/com/spring/step3/security/manger/PostAuthorizationManager.java index 711c5d3..640ac90 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/security/manger/PostAuthorizationManager.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/security/manger/PostAuthorizationManager.java @@ -1,48 +1,91 @@ package com.spring.step3.security.manger; +import com.spring.step3.domain.vo.result.Result; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.method.MethodInvocationResult; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.function.Supplier; + /** * 处理方法调用后的授权检查 * check()方法接收的是MethodInvocationResult对象,包含已执行方法的结果 * 用于决定是否允许返回某个方法的结果(后置过滤) * 这是Spring Security较新的"后置授权"功能 */ -// @Component -// public class PostAuthorizationManager implements AuthorizationManager { -// -// /** -// * 这里两个实现方法按照Security官方要求进行实现 -// *

类说明:

-// * 下面的实现是对方法执行前进行权限校验的判断 -// *
-//      *     AuthorizationManager <MethodInvocation>
-//      * 
-// * 下面的这个是对方法执行后对权限的判断 -// *
-//      *     AuthorizationManager <MethodInvocationResult>
-//      * 
-// * -// *

注意事项:

-// * 将上述两个方法按照自定义的方式进行实现后,还需要禁用默认的。 -// *
-//      * @Configuration
-//      * @EnableMethodSecurity(prePostEnabled = false)
-//      * class MethodSecurityConfig {
-//      *     @Bean
-//      *     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
-//      *    Advisor preAuthorize(MyAuthorizationManager manager) {
-//      * 		return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
-//      *    }
-//      *
-//      *    @Bean
-//      *    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
-//      *    Advisor postAuthorize(MyAuthorizationManager manager) {
-//      * 		return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
-//      *    }
-//      * }
-//      * 
-// */ -// @Override -// public AuthorizationDecision check(Supplier authentication, MethodInvocationResult invocation) { -// return new AuthorizationDecision(true); -// } -// } \ No newline at end of file +@Component +public class PostAuthorizationManager implements AuthorizationManager { + + /** + * 这里两个实现方法按照Security官方要求进行实现 + *

类说明:

+ * 下面的实现是对方法执行前进行权限校验的判断 + *
+     *     AuthorizationManager <MethodInvocation>
+     * 
+ * 下面的这个是对方法执行后对权限的判断 + *
+     *     AuthorizationManager <MethodInvocationResult>
+     * 
+ * + *

注意事项:

+ * 将上述两个方法按照自定义的方式进行实现后,还需要禁用默认的。 + *
+     * @Configuration
+     * @EnableMethodSecurity(prePostEnabled = false)
+     * class MethodSecurityConfig {
+     *     @Bean
+     *     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+     *    Advisor preAuthorize(MyAuthorizationManager manager) {
+     * 		return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
+     *    }
+     *
+     *    @Bean
+     *    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+     *    Advisor postAuthorize(MyAuthorizationManager manager) {
+     * 		return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
+     *    }
+     * }
+     * 
+ */ + @Override + public AuthorizationDecision check(Supplier authenticationSupplier, MethodInvocationResult methodInvocationResult) { + Authentication authentication = authenticationSupplier.get(); + + // 如果方法有 @PreAuthorize 注解,会先到这里 + if (authentication == null || !authentication.isAuthenticated()) { + return new AuthorizationDecision(false); + } + + // 检查权限 + boolean granted = hasPermission(authentication, methodInvocationResult); + return new AuthorizationDecision(granted); + } + + private boolean hasPermission(Authentication authentication, MethodInvocationResult methodInvocationResult) { + // 1. 获取当前校验方法的返回值 + if (methodInvocationResult.getResult() instanceof Result result) { + // 拿到当前返回值中权限内容 + List auths = result.getAuths(); + + // 判断返回值中返回方法全新啊是否和用户权限匹配 + return authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority) + .anyMatch(auth -> + // ❗这里是忽略了大小写匹配的 admin 权限,如果包含 admin 无论大小写都可以放行 + auth.equalsIgnoreCase("admin") + || auths.contains(auth) + ); + } + + // ❗这里可以设置自己的返回状态 + // ====================================== + // 默认返回 TRUE 是因为有可能当前方法不需要验证 + // 所以才设置默认返回为 TURE + // ====================================== + return true; + } +} \ No newline at end of file diff --git a/spring-security/step-3/src/main/java/com/spring/step3/security/manger/PreAuthorizationManager.java b/spring-security/step-3/src/main/java/com/spring/step3/security/manger/PreAuthorizationManager.java index 83de82f..a02224a 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/security/manger/PreAuthorizationManager.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/security/manger/PreAuthorizationManager.java @@ -1,49 +1,83 @@ package com.spring.step3.security.manger; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.function.Supplier; + /** * 处理方法调用前的授权检查 * check()方法接收的是MethodInvocation对象,包含即将执行的方法调用信息 * 用于决定是否允许执行某个方法 * 这是传统的"前置授权"模式 */ -// @Component -// public class PreAuthorizationManager implements AuthorizationManager { -// -// /** -// * 这里两个实现方法按照Security官方要求进行实现 -// *

类说明:

-// * 下面的实现是对方法执行前进行权限校验的判断 -// *
-//      *     AuthorizationManager <MethodInvocation>
-//      * 
-// * 下面的这个是对方法执行后对权限的判断 -// *
-//      *     AuthorizationManager <MethodInvocationResult>
-//      * 
-// * -// *

注意事项:

-// * 将上述两个方法按照自定义的方式进行实现后,还需要禁用默认的。 -// *
-//      * @Configuration
-//      * @EnableMethodSecurity(prePostEnabled = false)
-//      * class MethodSecurityConfig {
-//      *     @Bean
-//      *     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
-//      *    Advisor preAuthorize(MyAuthorizationManager manager) {
-//      * 		return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
-//      *    }
-//      *
-//      *    @Bean
-//      *    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
-//      *    Advisor postAuthorize(MyAuthorizationManager manager) {
-//      * 		return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
-//      *    }
-//      * }
-//      * 
-// */ -// @Override -// public AuthorizationDecision check(Supplier authentication, MethodInvocation invocation) { -// return new AuthorizationDecision(true); -// } -// -// } \ No newline at end of file +@Component +public class PreAuthorizationManager implements AuthorizationManager { + + /** + * 这里两个实现方法按照Security官方要求进行实现 + *

类说明:

+ * 下面的实现是对方法执行前进行权限校验的判断 + *
+     *     AuthorizationManager <MethodInvocation>
+     * 
+ * 下面的这个是对方法执行后对权限的判断 + *
+     *     AuthorizationManager <MethodInvocationResult>
+     * 
+ * + *

注意事项:

+ * 将上述两个方法按照自定义的方式进行实现后,还需要禁用默认的。 + *
+     * @Configuration
+     * @EnableMethodSecurity(prePostEnabled = false)
+     * class MethodSecurityConfig {
+     *     @Bean
+     *     @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+     *    Advisor preAuthorize(MyAuthorizationManager manager) {
+     * 		return AuthorizationManagerBeforeMethodInterceptor.preAuthorize(manager);
+     *    }
+     *
+     *    @Bean
+     *    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+     *    Advisor postAuthorize(MyAuthorizationManager manager) {
+     * 		return AuthorizationManagerAfterMethodInterceptor.postAuthorize(manager);
+     *    }
+     * }
+     * 
+ */ + @Override + public AuthorizationDecision check(Supplier authenticationSupplier, MethodInvocation methodInvocation) { + Authentication authentication = authenticationSupplier.get(); + + // 如果方法有 @PreAuthorize 注解,会先到这里 + if (authentication == null || !authentication.isAuthenticated()) { + return new AuthorizationDecision(false); + } + + // 检查权限 + boolean granted = hasPermission(authentication, methodInvocation); + return new AuthorizationDecision(granted); + } + + private boolean hasPermission(Authentication authentication, MethodInvocation methodInvocation) { + // 1. 获取方法上的权限注解(如果有) + // 例如:@PreAuthorize("hasRole('ADMIN')") 或其他自定义注解 + + // 2. 获取用户权限 + Collection authorities = authentication.getAuthorities(); + + // 3. 实现你的权限逻辑 + // 这里简单示例:检查方法名是否包含在权限中 + String methodName = methodInvocation.getMethod().getName(); + return authorities.stream() + .map(GrantedAuthority::getAuthority) + // ❗这里是忽略了大小写匹配的 admin 权限,如果包含 admin 无论大小写都可以放行 + .anyMatch(auth -> auth.equalsIgnoreCase("admin") || auth.equals(methodName)); + } +} \ No newline at end of file diff --git a/spring-security/step-3/src/main/resources/static/src/config/axios-config.js b/spring-security/step-3/src/main/resources/static/src/config/axios-config.js index 99c1932..f3e2a4b 100644 --- a/spring-security/step-3/src/main/resources/static/src/config/axios-config.js +++ b/spring-security/step-3/src/main/resources/static/src/config/axios-config.js @@ -1,6 +1,6 @@ // axios 配置 const axiosInstance = axios.create({ - baseURL: 'http://localhost:8772/api', + baseURL: 'http://localhost:8773/api', timeout: 16000, headers: {'Content-Type': 'application/json;charset=utf-8'}, });