diff --git a/spring-security/ReadMe.md b/spring-security/ReadMe.md index f4cee03..ae8d08a 100644 --- a/spring-security/ReadMe.md +++ b/spring-security/ReadMe.md @@ -1083,8 +1083,43 @@ public class AuthTestController { ## 其余授权方式 +### 前置条件 + +为了做一个较为通用看起来比较规范,写了一个配置类,可以在Spring配置文件中进行配置。 + +```java +@Getter +@Setter +@Configuration +@ConfigurationProperties(prefix = "security-path") +@Schema(name = "SecurityPathsProperties对象", description = "路径忽略和认证") +public class SecurityConfigProperties { + + @Schema(name = "noAuthPaths", description = "不用认证的路径") + public List noAuthPaths; + + @Schema(name = "securedPaths", description = "需要认证的路径") + public List securedPaths; + + @Schema(name = "允许的角色或权限", description = "允许的角色或权限") + public List adminAuthorities; + +} +``` + +之后在配置文件中指定哪些可以放行的角色或权限。 + +```yaml +security-path: + # 配置放行的角色或权限 + admin-authorities: + - "ADMIN" +``` + ### 通过编程方式授权方法 +#### 简单使用 + 如果需要对权限做出自定义的需求,将传入参数作为判断权限条件,这会很有用,比如某些参数不可以传入,或者参数做权限校验等。 首先创建一个Spring组件,包含自定义的授权逻辑: @@ -1127,45 +1162,66 @@ public Result lowerUser(String name) { } ``` +#### 进阶使用 + +> [!NOTE] +> +> 参考文档:[使用自定义的 `@Bean` 而不是对 `DefaultMethodSecurityExpressionHandler` 进行子类化](https://docs.spring.io/spring-security/reference/6.3/servlet/authorization/method-security.html#_use_a_custom_bean_instead_of_subclassing_defaultmethodsecurityexpressionhandler) + +在上面可以大致了解到如何使用自定义的方式进行扩展,好处是可以简单易用。 + +可以对比下下面章节**【使用自定义授权管理器实现自定义权限校验】**实现哪个更符合你期望预期。 + +```java +@Component("auth") +@RequiredArgsConstructor +public class AuthorizationLogic { + + private final SecurityConfigProperties securityConfigProperties; + + /** + * 基本权限检查 + */ + public boolean decide(String requiredAuthority) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication == null || !authentication.isAuthenticated()) { + return false; + } + + // 检查用户是否有指定权限或是admin + return hasAuthority(authentication, requiredAuthority) || isAdmin(authentication); + } + + /** + * 检查是否是管理员 + */ + public boolean isAdmin() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return authentication != null && isAdmin(authentication); + } + + private boolean isAdmin(Authentication authentication) { + return securityConfigProperties.getAdminAuthorities().stream() + .anyMatch(auth -> authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .anyMatch(ga -> ga.equals(auth))); + } + + private boolean hasAuthority(Authentication authentication, String authority) { + return authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .anyMatch(auth -> auth.equals(authority)); + } +} +``` + ### 使用自定义授权管理器实现自定义权限校验 在实际开发中对于SpringSecurity提供的两个权限校验注解`@PreAuthorize`和`@PostAuthorize`,需要对这两个进行覆盖或者改造,需要实现两个`AuthorizationManager`。 实现完成后需要显式的在配置中禁用原先的内容。 -#### 前置条件 - -为了做一个较为通用看起来比较规范,写了一个配置类,可以在Spring配置文件中进行配置。 - -```java -@Getter -@Setter -@Configuration -@ConfigurationProperties(prefix = "security-path") -@Schema(name = "SecurityPathsProperties对象", description = "路径忽略和认证") -public class SecurityConfigProperties { - - @Schema(name = "noAuthPaths", description = "不用认证的路径") - public List noAuthPaths; - - @Schema(name = "securedPaths", description = "需要认证的路径") - public List securedPaths; - - @Schema(name = "允许的角色或权限", description = "允许的角色或权限") - public List adminAuthorities; - -} -``` - -之后在配置文件中指定哪些可以放行的角色或权限。 - -```yaml -security-path: - # 配置放行的角色或权限 - admin-authorities: - - "ADMIN" -``` - #### 1. 实现前置 **方式一:正则表达式** diff --git a/spring-security/step-3/src/main/java/com/spring/step3/controller/test/SecurityProgrammaticallyController.java b/spring-security/step-3/src/main/java/com/spring/step3/controller/test/SecurityProgrammaticallyController.java index 3171ed9..c55ada5 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/controller/test/SecurityProgrammaticallyController.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/controller/test/SecurityProgrammaticallyController.java @@ -3,7 +3,6 @@ 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 jakarta.annotation.security.PermitAll; import lombok.extern.slf4j.Slf4j; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; @@ -17,8 +16,7 @@ import org.springframework.web.bind.annotation.RestController; @PreAuthorize("hasAuthority('USER')") public class SecurityProgrammaticallyController { - // @PreAuthorize("permitAll()") - @PermitAll + @PreAuthorize("permitAll()") @Operation(summary = "拥有 USER 的角色可以访问", description = "当前用户拥有 USER 角色可以访问这个接口") @GetMapping("upper-user") public Result upperUser() { diff --git a/spring-security/step-3/src/main/java/com/spring/step3/mapper/AuthLogMapper.java b/spring-security/step-3/src/main/java/com/spring/step3/mapper/AuthLogMapper.java index 4bbe59b..b88fee0 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/mapper/AuthLogMapper.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/mapper/AuthLogMapper.java @@ -1,6 +1,5 @@ package com.spring.step3.mapper; - import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; diff --git a/spring-security/step-3/src/main/java/com/spring/step3/security/annotation/AuthorizationLogic.java b/spring-security/step-3/src/main/java/com/spring/step3/security/annotation/AuthorizationLogic.java index 0e2af49..cd22e9f 100644 --- a/spring-security/step-3/src/main/java/com/spring/step3/security/annotation/AuthorizationLogic.java +++ b/spring-security/step-3/src/main/java/com/spring/step3/security/annotation/AuthorizationLogic.java @@ -1,14 +1,49 @@ package com.spring.step3.security.annotation; +import com.spring.step3.security.config.properties.SecurityConfigProperties; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; @Component("auth") +@RequiredArgsConstructor public class AuthorizationLogic { - public boolean decide(String name) { - // 直接使用name的实现 - // System.out.println(name); - return name.equalsIgnoreCase("user"); + private final SecurityConfigProperties securityConfigProperties; + + /** + * 基本权限检查 + */ + public boolean decide(String requiredAuthority) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + if (authentication == null || !authentication.isAuthenticated()) { + return false; + } + + // 检查用户是否有指定权限或是admin + boolean baseAuthority = authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .anyMatch(auth -> auth.equals(requiredAuthority)); + + return baseAuthority || isAdmin(authentication); + } + + /** + * 检查是否是管理员 + */ + public boolean isAdmin() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return authentication != null && isAdmin(authentication); + } + + private boolean isAdmin(Authentication authentication) { + return securityConfigProperties.getAdminAuthorities().stream() + .anyMatch(auth -> authentication.getAuthorities().stream() + .map(GrantedAuthority::getAuthority) + .anyMatch(ga -> ga.equals(auth))); } } \ No newline at end of file