🐛 用户Mapper判断写错
This commit is contained in:
parent
44732e6352
commit
84a053fdb6
|
@ -340,4 +340,140 @@ public UserDetails getCurrentUserDetail() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 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()`
|
|
@ -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()
|
// 其余请求都放行
|
||||||
// .requestMatchers("/api/security/**").permitAll()
|
.anyRequest().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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
// 返回响应
|
// 返回响应
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
// 返回响应
|
// 返回响应
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue