From 84a053fdb6b73ff70b300913054341f0d89761a4 Mon Sep 17 00:00:00 2001 From: bunny <1319900154@qq.com> Date: Mon, 14 Jul 2025 19:37:32 +0800 Subject: [PATCH] =?UTF-8?q?:bug:=20=E7=94=A8=E6=88=B7Mapper=E5=88=A4?= =?UTF-8?q?=E6=96=AD=E5=86=99=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spring-security/ReadMe.md | 138 +++++++++++++++++- .../config/SecurityWebConfiguration.java | 29 ++-- .../handler/SecurityAccessDeniedHandler.java | 5 +- .../SecurityAuthenticationEntryPoint.java | 8 +- .../security/service/DbUserDetailService.java | 2 + .../src/main/resources/mapper/UserMapper.xml | 4 +- 6 files changed, 164 insertions(+), 22 deletions(-) diff --git a/spring-security/ReadMe.md b/spring-security/ReadMe.md index fa762f8..d8ed209 100644 --- a/spring-security/ReadMe.md +++ b/spring-security/ReadMe.md @@ -340,4 +340,140 @@ public UserDetails getCurrentUserDetail() { return null; } } -``` \ No newline at end of file +``` + +## URL资源认证配置 + +### 角色与权限配置 + +#### 1. 基于角色的URL访问控制 + +##### 单角色配置 + +配置`/api/**`路径下的所有接口需要`ADMIN`角色才能访问: + +```java +@Bean +SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(authorize -> authorize + // 注意:会自动添加"ROLE_"前缀,实际检查的是ROLE_ADMIN + .requestMatchers("/api/**").hasRole("ADMIN") + ) + // 其他配置... + ; + return http.build(); +} +``` + +##### 多角色配置(满足任一角色即可访问) + +```java +@Bean +SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(authorize -> authorize + // 检查是否有ADMIN或USER角色(自动添加ROLE_前缀) + .requestMatchers("/api/**").hasAnyRole("ADMIN", "USER") + ) + // 其他配置... + ; + return http.build(); +} +``` + +#### 2. 基于权限的URL访问控制 + +##### 需要所有指定权限 + +```java +@Bean +SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(authorize -> authorize + // 需要同时拥有"all"和"read"权限 + .requestMatchers("/api/**").hasAuthority("all") + .requestMatchers("/api/**").hasAuthority("read") + ) + // 其他配置... + ; + return http.build(); +} +``` + +##### 满足任一权限即可 + +```java +@Bean +SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(authorize -> authorize + // 拥有"all"或"read"任一权限即可访问 + .requestMatchers("/api/**").hasAnyAuthority("all", "read") + ) + // 其他配置... + ; + return http.build(); +} +``` + +### 综合配置策略 + +#### 1. 基本配置模式 + +```java +@Bean +SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.authorizeHttpRequests(authorize -> authorize + // 特定路径需要认证 + .requestMatchers("/api/**").authenticated() + // 其他请求全部放行 + .anyRequest().permitAll() + ) + // 其他配置... + ; + return http.build(); +} +``` + +#### 2. 多路径匹配配置 + +```java +@Bean +SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + // 定义无需认证的白名单路径 + String[] permitAllUrls = { + "/", "/doc.html/**", + "/webjars/**", "/images/**", + "/.well-known/**", "/favicon.ico", + "/error/**", "/swagger-ui/**", + "/v3/api-docs/**" + }; + + http.authorizeHttpRequests(authorize -> authorize + // API路径需要认证 + .requestMatchers("/api/**").authenticated() + // 白名单路径直接放行 + .requestMatchers(permitAllUrls).permitAll() + // 其他请求需要登录(非匿名访问) + .anyRequest().authenticated() + ) + // 其他配置... + ; + return http.build(); +} +``` + +### 重要说明 + +1. **角色与权限的区别**: + - `hasRole()`会自动添加"ROLE_"前缀 + - `hasAuthority()`直接使用指定的权限字符串 +2. **匹配顺序**: + - Spring Security会按照配置的顺序进行匹配 + - 更具体的路径应该放在前面,通用规则(如anyRequest)放在最后 +3. **方法选择建议**: + - `hasRole()`/`hasAnyRole()`:适合基于角色的访问控制 + - `hasAuthority()`/`hasAnyAuthority()`:适合更细粒度的权限控制 + - `authenticated()`:只需认证通过,不检查具体角色/权限 + - `permitAll()`:完全开放访问 +4. **最佳实践**: + - 对于REST API,通常使用`authenticated()`配合方法级权限控制 + - 静态资源应明确配置`permitAll()` + - 生产环境不建议使用`anyRequest().permitAll()` \ No newline at end of file diff --git a/spring-security/step-2/src/main/java/com/spring/step2/security/config/SecurityWebConfiguration.java b/spring-security/step-2/src/main/java/com/spring/step2/security/config/SecurityWebConfiguration.java index bdb2aea..b68aad1 100644 --- a/spring-security/step-2/src/main/java/com/spring/step2/security/config/SecurityWebConfiguration.java +++ b/spring-security/step-2/src/main/java/com/spring/step2/security/config/SecurityWebConfiguration.java @@ -2,6 +2,8 @@ package com.spring.step2.security.config; import com.spring.step2.security.handler.SecurityAccessDeniedHandler; import com.spring.step2.security.handler.SecurityAuthenticationEntryPoint; +import com.spring.step2.security.service.DbUserDetailService; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; @@ -13,27 +15,21 @@ import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity @EnableMethodSecurity +@RequiredArgsConstructor public class SecurityWebConfiguration { + private final DbUserDetailService dbUserDetailService; + @Bean SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - String[] permitAllUrls = { - "/", "/doc.html/**", - "/webjars/**", "/images/**", ".well-known/**", "favicon.ico", "/error/**", - "/swagger-ui/**", "/v3/api-docs/**" - }; http.authorizeHttpRequests(authorizeRequests -> - // 访问路径为 /api 时需要进行认证 - authorizeRequests - .requestMatchers(permitAllUrls).permitAll() - // .requestMatchers("/api/**").hasAnyRole("Admin", "ADMIN", "admin") - .anyRequest().permitAll() - // .requestMatchers("/api/security/**").permitAll() - // .requestMatchers(HttpMethod.GET, "/api/anonymous/**").anonymous() - // // 会自动变成 ROLE_ADMIN - // // .requestMatchers("/api/**").hasRole("ADMIN") - // .requestMatchers("/api/**").hasAnyAuthority("all", "read") + // 访问路径为 /api 时需要进行认证 + authorizeRequests + // 只认证 /api/** 下的所有接口 + .requestMatchers("/api/**").authenticated() + // 其余请求都放行 + .anyRequest().permitAll() ) .formLogin(loginPage -> loginPage // 自定义登录页路径 @@ -56,9 +52,12 @@ public class SecurityWebConfiguration { ) .csrf(AbstractHttpConfigurer::disable) .exceptionHandling(configurer -> configurer + // 自定无权访问返回内容 .accessDeniedHandler(new SecurityAccessDeniedHandler()) + // 自定义未授权返回内容 .authenticationEntryPoint(new SecurityAuthenticationEntryPoint()) ) + .userDetailsService(dbUserDetailService) ; return http.build(); diff --git a/spring-security/step-2/src/main/java/com/spring/step2/security/handler/SecurityAccessDeniedHandler.java b/spring-security/step-2/src/main/java/com/spring/step2/security/handler/SecurityAccessDeniedHandler.java index a0cb48a..04ecb9e 100644 --- a/spring-security/step-2/src/main/java/com/spring/step2/security/handler/SecurityAccessDeniedHandler.java +++ b/spring-security/step-2/src/main/java/com/spring/step2/security/handler/SecurityAccessDeniedHandler.java @@ -2,6 +2,7 @@ package com.spring.step2.security.handler; import com.alibaba.fastjson2.JSON; import com.spring.step2.domain.vo.result.Result; +import com.spring.step2.domain.vo.result.ResultCodeEnum; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -17,8 +18,10 @@ public class SecurityAccessDeniedHandler implements AccessDeniedHandler { public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { log.error("CustomerAccessDeniedHandler:{}", accessDeniedException.getLocalizedMessage()); - Result result = Result.error(accessDeniedException.getMessage()); + // 无权访问接口 + Result result = Result.error(accessDeniedException.getMessage(), ResultCodeEnum.FAIL_NO_ACCESS_DENIED); + // 转成JSON格式 Object json = JSON.toJSON(result); // 返回响应 diff --git a/spring-security/step-2/src/main/java/com/spring/step2/security/handler/SecurityAuthenticationEntryPoint.java b/spring-security/step-2/src/main/java/com/spring/step2/security/handler/SecurityAuthenticationEntryPoint.java index d54322e..cc1f048 100644 --- a/spring-security/step-2/src/main/java/com/spring/step2/security/handler/SecurityAuthenticationEntryPoint.java +++ b/spring-security/step-2/src/main/java/com/spring/step2/security/handler/SecurityAuthenticationEntryPoint.java @@ -2,7 +2,7 @@ package com.spring.step2.security.handler; import com.alibaba.fastjson2.JSON; import com.spring.step2.domain.vo.result.Result; -import jakarta.servlet.ServletException; +import com.spring.step2.domain.vo.result.ResultCodeEnum; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; @@ -15,11 +15,13 @@ import java.io.IOException; public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint { @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { log.error("CustomerAccessDeniedHandler:{}", authException.getLocalizedMessage()); - Result result = Result.error(authException.getMessage()); + // 未认证---未登录 + Result result = Result.error(authException.getMessage(), ResultCodeEnum.LOGIN_AUTH); + // 将错误的请求转成JSON Object json = JSON.toJSON(result); // 返回响应 diff --git a/spring-security/step-2/src/main/java/com/spring/step2/security/service/DbUserDetailService.java b/spring-security/step-2/src/main/java/com/spring/step2/security/service/DbUserDetailService.java index 5446adf..8927b0a 100644 --- a/spring-security/step-2/src/main/java/com/spring/step2/security/service/DbUserDetailService.java +++ b/spring-security/step-2/src/main/java/com/spring/step2/security/service/DbUserDetailService.java @@ -1,5 +1,6 @@ package com.spring.step2.security.service; +import com.baomidou.dynamic.datasource.annotation.DS; import com.spring.step2.domain.entity.PermissionEntity; import com.spring.step2.domain.entity.UserEntity; import com.spring.step2.mapper.UserMapper; @@ -13,6 +14,7 @@ import org.springframework.stereotype.Service; import java.util.List; +@DS("testJwt") @Service @RequiredArgsConstructor public class DbUserDetailService implements UserDetailsService { diff --git a/spring-security/step-2/src/main/resources/mapper/UserMapper.xml b/spring-security/step-2/src/main/resources/mapper/UserMapper.xml index 9e5c788..d19c6b0 100644 --- a/spring-security/step-2/src/main/resources/mapper/UserMapper.xml +++ b/spring-security/step-2/src/main/resources/mapper/UserMapper.xml @@ -51,8 +51,8 @@ from t_user - - username=#{username} + + username = #{username}