🐛 用户Mapper判断写错

This commit is contained in:
bunny 2025-07-14 19:37:32 +08:00
parent 44732e6352
commit 84a053fdb6
6 changed files with 164 additions and 22 deletions

View File

@ -341,3 +341,139 @@ public UserDetails getCurrentUserDetail() {
} }
} }
``` ```
## 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()`

View File

@ -2,6 +2,8 @@ package com.spring.step2.security.config;
import com.spring.step2.security.handler.SecurityAccessDeniedHandler; import com.spring.step2.security.handler.SecurityAccessDeniedHandler;
import com.spring.step2.security.handler.SecurityAuthenticationEntryPoint; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
@ -13,27 +15,21 @@ import org.springframework.security.web.SecurityFilterChain;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
@EnableMethodSecurity @EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityWebConfiguration { public class SecurityWebConfiguration {
private final DbUserDetailService dbUserDetailService;
@Bean @Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception { 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 -> http.authorizeHttpRequests(authorizeRequests ->
// 访问路径为 /api 时需要进行认证 // 访问路径为 /api 时需要进行认证
authorizeRequests authorizeRequests
.requestMatchers(permitAllUrls).permitAll() // 只认证 /api/** 下的所有接口
// .requestMatchers("/api/**").hasAnyRole("Admin", "ADMIN", "admin") .requestMatchers("/api/**").authenticated()
// 其余请求都放行
.anyRequest().permitAll() .anyRequest().permitAll()
// .requestMatchers("/api/security/**").permitAll()
// .requestMatchers(HttpMethod.GET, "/api/anonymous/**").anonymous()
// // 会自动变成 ROLE_ADMIN
// // .requestMatchers("/api/**").hasRole("ADMIN")
// .requestMatchers("/api/**").hasAnyAuthority("all", "read")
) )
.formLogin(loginPage -> loginPage .formLogin(loginPage -> loginPage
// 自定义登录页路径 // 自定义登录页路径
@ -56,9 +52,12 @@ public class SecurityWebConfiguration {
) )
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.exceptionHandling(configurer -> configurer .exceptionHandling(configurer -> configurer
// 自定无权访问返回内容
.accessDeniedHandler(new SecurityAccessDeniedHandler()) .accessDeniedHandler(new SecurityAccessDeniedHandler())
// 自定义未授权返回内容
.authenticationEntryPoint(new SecurityAuthenticationEntryPoint()) .authenticationEntryPoint(new SecurityAuthenticationEntryPoint())
) )
.userDetailsService(dbUserDetailService)
; ;
return http.build(); return http.build();

View File

@ -2,6 +2,7 @@ package com.spring.step2.security.handler;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.spring.step2.domain.vo.result.Result; import com.spring.step2.domain.vo.result.Result;
import com.spring.step2.domain.vo.result.ResultCodeEnum;
import jakarta.servlet.ServletException; import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; 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 { public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
log.error("CustomerAccessDeniedHandler:{}", accessDeniedException.getLocalizedMessage()); log.error("CustomerAccessDeniedHandler:{}", accessDeniedException.getLocalizedMessage());
Result<Object> result = Result.error(accessDeniedException.getMessage()); // 无权访问接口
Result<Object> result = Result.error(accessDeniedException.getMessage(), ResultCodeEnum.FAIL_NO_ACCESS_DENIED);
// 转成JSON格式
Object json = JSON.toJSON(result); Object json = JSON.toJSON(result);
// 返回响应 // 返回响应

View File

@ -2,7 +2,7 @@ package com.spring.step2.security.handler;
import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSON;
import com.spring.step2.domain.vo.result.Result; 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.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -15,11 +15,13 @@ import java.io.IOException;
public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint { public class SecurityAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override @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()); log.error("CustomerAccessDeniedHandler:{}", authException.getLocalizedMessage());
Result<Object> result = Result.error(authException.getMessage()); // 未认证---未登录
Result<Object> result = Result.error(authException.getMessage(), ResultCodeEnum.LOGIN_AUTH);
// 将错误的请求转成JSON
Object json = JSON.toJSON(result); Object json = JSON.toJSON(result);
// 返回响应 // 返回响应

View File

@ -1,5 +1,6 @@
package com.spring.step2.security.service; 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.PermissionEntity;
import com.spring.step2.domain.entity.UserEntity; import com.spring.step2.domain.entity.UserEntity;
import com.spring.step2.mapper.UserMapper; import com.spring.step2.mapper.UserMapper;
@ -13,6 +14,7 @@ import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
@DS("testJwt")
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class DbUserDetailService implements UserDetailsService { public class DbUserDetailService implements UserDetailsService {

View File

@ -51,8 +51,8 @@
<include refid="Base_Column_List"/> <include refid="Base_Column_List"/>
from t_user from t_user
<where> <where>
<if test="username != null and username != ;;"> <if test="username != null and username != null">
username=#{username} username = #{username}
</if> </if>
</where> </where>
</select> </select>