添加接口权限校验

This commit is contained in:
2024-09-06 19:41:00 +08:00
parent d9f709de3d
commit 6f3f2f987c
11 changed files with 40 additions and 88 deletions
@@ -1,7 +1,6 @@
package com.gw.test.config; package com.gw.test.config;
import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@@ -18,6 +17,6 @@ public class OpenApiConfig {
return new OpenAPI().info(new Info().title("Test").version("v1.0.0").description( "接口测试")) return new OpenAPI().info(new Info().title("Test").version("v1.0.0").description( "接口测试"))
.addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION)) .addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION))
.components(new Components().addSecuritySchemes(HttpHeaders.AUTHORIZATION,new SecurityScheme() .components(new Components().addSecuritySchemes(HttpHeaders.AUTHORIZATION,new SecurityScheme()
.name(HttpHeaders.AUTHORIZATION).type(SecurityScheme.Type.HTTP).scheme("Bearer"))); .name(HttpHeaders.AUTHORIZATION).type(SecurityScheme.Type.HTTP).scheme("Bearer ")));
} }
} }
@@ -4,10 +4,8 @@ import com.gw.test.filters.*;
import com.gw.test.utils.JWTUtil; import com.gw.test.utils.JWTUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
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.context.annotation.Lazy;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
@@ -27,7 +25,6 @@ public class SecurityBeanConfig {
@Bean @Bean
public JWTUtil jWTUtil() { public JWTUtil jWTUtil() {
log.error("jwtUtil");
return new JWTUtil(); return new JWTUtil();
} }
@@ -46,25 +43,26 @@ public class SecurityBeanConfig {
return new JwtAuthenticationTokenFilter(userDetailsService,jwtTokenUtil); return new JwtAuthenticationTokenFilter(userDetailsService,jwtTokenUtil);
} }
@ConditionalOnBean(name = "dynamicSecurityService") // @ConditionalOnBean(name = "dynamicSecurityService")
@Bean @Bean
@Autowired @Autowired
public DynamicAccessDecisionManager dynamicAccessDecisionManager() { public DynamicAccessDecisionManager dynamicAccessDecisionManager() {
return new DynamicAccessDecisionManager(); return new DynamicAccessDecisionManager();
} }
@ConditionalOnBean(name = "dynamicSecurityService") // @ConditionalOnBean(name = "dynamicSecurityService")
@Bean @Bean
@Autowired // @Lazy
public DynamicSecurityMetadataSource dynamicSecurityMetadataSource(DynamicSecurityService dynamicSecurityService) { public DynamicSecurityMetadataSource dynamicSecurityMetadataSource(DynamicSecurityService dynamicSecurityService) {
// log.error("=============={}",dynamicSecurityService.loadDataSource());
return new DynamicSecurityMetadataSource(dynamicSecurityService); return new DynamicSecurityMetadataSource(dynamicSecurityService);
} }
@ConditionalOnBean(name = "dynamicSecurityService") // @ConditionalOnBean(name = "dynamicSecurityService")
@Bean @Bean
@Lazy(value = false) // @Lazy
public DynamicSecurityFilter dynamicSecurityFilter(DynamicSecurityMetadataSource dynamicSecurityMetadataSource,IgnoreUrlsConfig ignoreUrlsConfig){ public DynamicSecurityFilter dynamicSecurityFilter(DynamicSecurityMetadataSource dynamicSecurityMetadataSource,IgnoreUrlsConfig ignoreUrlsConfig){
log.error("动态权限注解支持启动"); // log.error("======动态权限注解支持启动========{}",dynamicSecurityMetadataSource);
return new DynamicSecurityFilter(dynamicSecurityMetadataSource,ignoreUrlsConfig); return new DynamicSecurityFilter(dynamicSecurityMetadataSource,ignoreUrlsConfig);
} }
} }
@@ -2,12 +2,10 @@ package com.gw.test.config;
import com.gw.test.filters.*; import com.gw.test.filters.*;
import com.gw.test.utils.JWTUtil; import com.gw.test.utils.JWTUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -15,10 +13,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer; import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@@ -27,7 +22,7 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
@EnableWebSecurity @EnableWebSecurity
@RequiredArgsConstructor @RequiredArgsConstructor
public class SecurityConfig { public class SecurityConfig {
private final JWTUtil jwtUtil; // private final JWTUtil jwtUtil;
private final IgnoreUrlsConfig ignoreUrlsConfig; private final IgnoreUrlsConfig ignoreUrlsConfig;
@@ -45,32 +40,6 @@ public class SecurityConfig {
@Bean @Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception{
// ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity.authorizeRequests();
// //不需要保护的资源路径允许访问
// for (String url : ignoreUrlsConfig.getUrls()) {
// log.error("[{}] [{}]", "白名单路径", url);
// registry.requestMatchers(url).permitAll();
// }
// //允许跨域请求的OPTIONS请求
// registry.requestMatchers(HttpMethod.OPTIONS)
// .permitAll();
// // 任何请求需要身份认证
// registry.and()
// .authorizeRequests()
// .anyRequest()
// .authenticated()
// // 关闭跨站请求防护及不使用session
// .and()
// .csrf(AbstractHttpConfigurer::disable)
// .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
// .exceptionHandling(exception -> exception
// .accessDeniedHandler(restfulAccessDeniedHandler)
// .authenticationEntryPoint(restAuthenticationEntryPoint))
// // 自定义JWT认证过滤器
// .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// // 添加动态权限校验过滤器
//// .addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
// return httpSecurity.build();
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
@@ -94,31 +63,8 @@ public class SecurityConfig {
.accessDeniedHandler(restfulAccessDeniedHandler) .accessDeniedHandler(restfulAccessDeniedHandler)
.authenticationEntryPoint(restAuthenticationEntryPoint)) .authenticationEntryPoint(restAuthenticationEntryPoint))
// 自定义JWT认证过滤器 // 自定义JWT认证过滤器
.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class)
// 添加动态权限校验过滤器 .addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
// if (dynamicSecurityService != null)
// registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
// .addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
return httpSecurity.build(); return httpSecurity.build();
} }
//
// @Bean
// public PasswordEncoder passwordEncoder() {
// // 密码加密方式
// return new BCryptPasswordEncoder();
// }
// @Bean
// public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
// .exceptionHandling()
// .accessDeniedHandler(restfulAccessDeniedHandler)
// .authenticationEntryPoint(restAuthenticationEntryPoint)
// // 自定义权限拦截器JWT过滤器
// .and()
// .addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
//有动态权限配置时添加动态权限校验过滤器
// if(dynamicSecurityService != null){
// registry.and().addFilterBefore(dynamicSecurityFilter, FilterSecurityInterceptor.class);
// }
// }
} }
@@ -3,21 +3,21 @@ package com.gw.test.config;
import com.gw.test.filters.DynamicSecurityService; import com.gw.test.filters.DynamicSecurityService;
import com.gw.test.service.IGuoWeiService; import com.gw.test.service.IGuoWeiService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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.access.ConfigAttribute; import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig; import org.springframework.security.access.SecurityConfig;
import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UserDetailsService;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
* 后台用户安全配置 * 后台用户安全配置
*/ */
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Configuration @Configuration
public class UserSecurityConfig { public class UserSecurityConfig {
@@ -34,15 +34,17 @@ public class UserSecurityConfig {
@Bean @Bean
public DynamicSecurityService dynamicSecurityService() { public DynamicSecurityService dynamicSecurityService() {
log.error("========动态权限配置加载========");
return () -> { return () -> {
Map<String, ConfigAttribute> map = new ConcurrentHashMap<>(); Map<String, ConfigAttribute> map = new ConcurrentHashMap<>();
Map<String,String> apis = new HashMap<>(); Map<String,String> apis = new HashMap<>();
apis.put("查询用户","ROLE_USER"); apis.put("/guowei/list","查询所有");
apis.put("新增用户","ROLE_ADMIN"); apis.put("/guowei/update","修改");
apis.put("删除用户","ROLE_ADMIN"); apis.put("/guowei/delete","删除");
apis.put("修改用户","ROLE_ADMIN"); apis.put("/guowei/add","添加");
for (String api : apis.keySet()) { for (String api : apis.keySet()) {
map.put(api,new SecurityConfig(apis.get(api))); // map.put(api,new SecurityConfig(apis.get(api)));
map.put(api,new SecurityConfig(api));
} }
return map; return map;
}; };
+5 -1
View File
@@ -2,7 +2,7 @@ package com.gw.test.entity;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import java.time.LocalDateTime; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@@ -25,6 +25,7 @@ import lombok.experimental.Accessors;
@Schema($schema = "GuoWei对象",description = "") @Schema($schema = "GuoWei对象",description = "")
public class GuoWei implements Serializable { public class GuoWei implements Serializable {
@Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Schema($schema = "主键") @Schema($schema = "主键")
@@ -44,5 +45,8 @@ public class GuoWei implements Serializable {
@Schema($schema = "手机号") @Schema($schema = "手机号")
private String phoneNumber; private String phoneNumber;
@Schema($schema = "权限")
private String permissions;
} }
+4 -2
View File
@@ -3,21 +3,23 @@ package com.gw.test.entity;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
public class User implements UserDetails { public class User implements UserDetails {
private GuoWei entity; private GuoWei entity;
private List<GrantedAuthority> authorities; private List<String> authorities;
@Override @Override
public Collection<? extends GrantedAuthority> getAuthorities() { public Collection<? extends GrantedAuthority> getAuthorities() {
return this.authorities; return this.authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
} }
@Override @Override
@@ -1,6 +1,7 @@
package com.gw.test.filters; package com.gw.test.filters;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.ConfigAttribute;
@@ -9,11 +10,11 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator;
/** /**
* 判断用户是否有访问权限 * 判断用户是否有访问权限
*/ */
@Slf4j
public class DynamicAccessDecisionManager implements AccessDecisionManager { public class DynamicAccessDecisionManager implements AccessDecisionManager {
@Override @Override
@@ -30,6 +31,7 @@ public class DynamicAccessDecisionManager implements AccessDecisionManager {
//将访问所需资源或用户拥有资源进行比对 //将访问所需资源或用户拥有资源进行比对
String needAuthority = configAttribute.getAttribute(); String needAuthority = configAttribute.getAttribute();
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) { for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
log.error("needAuthority:{},grantedAuthority:{}", needAuthority, grantedAuthority.getAuthority());
if (needAuthority.trim().equals(grantedAuthority.getAuthority())) { if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
return; return;
} }
@@ -1,7 +1,5 @@
package com.gw.test.filters; package com.gw.test.filters;
//import com.gw.security.config.IgnoreUrlsConfig;
//import jakarta.servlet.Filter;
import com.gw.test.config.IgnoreUrlsConfig; import com.gw.test.config.IgnoreUrlsConfig;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -43,7 +41,7 @@ public class DynamicSecurityFilter extends AbstractSecurityInterceptor implement
@Override @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
log.error("进入动态权限过滤器"); // log.error("进入动态权限过滤器");
HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletRequest request = (HttpServletRequest) servletRequest;
FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain); FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
//OPTIONS请求直接放行 //OPTIONS请求直接放行
@@ -54,8 +52,9 @@ public class DynamicSecurityFilter extends AbstractSecurityInterceptor implement
//白名单请求直接放行 //白名单请求直接放行
PathMatcher pathMatcher = new AntPathMatcher(); PathMatcher pathMatcher = new AntPathMatcher();
for (String path : ignoreUrlsConfig.getUrls()) { for (String path : ignoreUrlsConfig.getUrls()) {
log.error("=============>ignor:{},{}",path,request.getRequestURI()); // log.error("=============>ignor:{},{}",path,request.getRequestURI());
if(pathMatcher.match(path,request.getRequestURI())){ if(pathMatcher.match(path,request.getRequestURI())){
log.error("=============>白名单请求直接放行{},{}",request.getRequestURI(),path);
fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
return; return;
} }
@@ -2,10 +2,8 @@ package com.gw.test.filters;
import cn.hutool.core.util.URLUtil; import cn.hutool.core.util.URLUtil;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import lombok.Data;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
@@ -42,7 +40,7 @@ public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMe
//获取当前访问的路径 //获取当前访问的路径
String url = ((FilterInvocation) o).getRequestUrl(); String url = ((FilterInvocation) o).getRequestUrl();
String path = URLUtil.getPath(url); String path = URLUtil.getPath(url);
log.error("------DynamicSecurityMetadataSource:请求url:{}",url); // log.error("------DynamicSecurityMetadataSource:请求url:{}",url);
PathMatcher pathMatcher = new AntPathMatcher(); PathMatcher pathMatcher = new AntPathMatcher();
//获取访问该路径所需资源 //获取访问该路径所需资源
for (String pattern : configAttributeMap.keySet()) { for (String pattern : configAttributeMap.keySet()) {
@@ -15,6 +15,7 @@ import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.List; import java.util.List;
/** /**
@@ -38,8 +39,8 @@ public class GuoWeiServiceImpl extends ServiceImpl<GuoWeiMapper, GuoWei> impleme
wrapper.eq("username", s); wrapper.eq("username", s);
List<GuoWei> list = list(wrapper); List<GuoWei> list = list(wrapper);
if (list.isEmpty()) return null; if (list.isEmpty()) return null;
GuoWei guoWei = list.get(0); GuoWei guoWei = list.getFirst();
return new User(guoWei, null); return new User(guoWei, Arrays.stream(guoWei.getPermissions().split(",")).toList());
} }
@Override @Override
@@ -9,6 +9,7 @@
<result column="password" property="password" /> <result column="password" property="password" />
<result column="create_time" property="createTime" /> <result column="create_time" property="createTime" />
<result column="phone_number" property="phoneNumber" /> <result column="phone_number" property="phoneNumber" />
<result column="permissions" property="permissions" />
</resultMap> </resultMap>
</mapper> </mapper>