添加基于路径的动态权限支持
This commit is contained in:
parent
167e2d6ce6
commit
f1bec5df7f
@ -0,0 +1,51 @@
|
|||||||
|
package com.macro.mall.security.component;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import org.springframework.security.access.AccessDecisionManager;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
import org.springframework.security.authentication.InsufficientAuthenticationException;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.GrantedAuthority;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态权限决策管理器,用于判断用户是否有访问权限
|
||||||
|
* Created by macro on 2020/2/7.
|
||||||
|
*/
|
||||||
|
public class DynamicAccessDecisionManager implements AccessDecisionManager {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decide(Authentication authentication, Object object,
|
||||||
|
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
|
||||||
|
// 当接口未被配置资源时直接放行
|
||||||
|
if (CollUtil.isEmpty(configAttributes)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Iterator<ConfigAttribute> iterator = configAttributes.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
ConfigAttribute configAttribute = iterator.next();
|
||||||
|
//将访问所需资源或用户拥有资源进行比对
|
||||||
|
String needAuthority = configAttribute.getAttribute();
|
||||||
|
for (GrantedAuthority grantedAuthority : authentication.getAuthorities()) {
|
||||||
|
if (needAuthority.trim().equals(grantedAuthority.getAuthority())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new AccessDeniedException("抱歉,您没有访问权限");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(ConfigAttribute configAttribute) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> aClass) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
package com.macro.mall.security.component;
|
||||||
|
|
||||||
|
import com.macro.mall.security.config.IgnoreUrlsConfig;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.security.access.SecurityMetadataSource;
|
||||||
|
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
|
||||||
|
import org.springframework.security.access.intercept.InterceptorStatusToken;
|
||||||
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
import org.springframework.util.PathMatcher;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态权限过滤器,用于实现基于路径的动态权限过滤
|
||||||
|
* Created by macro on 2020/2/7.
|
||||||
|
*/
|
||||||
|
public class DynamicSecurityFilter extends AbstractSecurityInterceptor implements Filter {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
|
||||||
|
@Autowired
|
||||||
|
private IgnoreUrlsConfig ignoreUrlsConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setMyAccessDecisionManager(DynamicAccessDecisionManager dynamicAccessDecisionManager) {
|
||||||
|
super.setAccessDecisionManager(dynamicAccessDecisionManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(FilterConfig filterConfig) throws ServletException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||||
|
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
|
FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
|
||||||
|
//OPTIONS请求直接放行
|
||||||
|
if(request.getMethod().equals(HttpMethod.OPTIONS.toString())){
|
||||||
|
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//白名单请求直接放行
|
||||||
|
PathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
for (String path : ignoreUrlsConfig.getUrls()) {
|
||||||
|
if(pathMatcher.match(path,request.getRequestURI())){
|
||||||
|
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//此处会调用AccessDecisionManager中的decide方法进行鉴权操作
|
||||||
|
InterceptorStatusToken token = super.beforeInvocation(fi);
|
||||||
|
try {
|
||||||
|
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
|
||||||
|
} finally {
|
||||||
|
super.afterInvocation(token, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getSecureObjectClass() {
|
||||||
|
return FilterInvocation.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SecurityMetadataSource obtainSecurityMetadataSource() {
|
||||||
|
return dynamicSecurityMetadataSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package com.macro.mall.security.component;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.URLUtil;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
import org.springframework.security.web.FilterInvocation;
|
||||||
|
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
|
||||||
|
import org.springframework.util.AntPathMatcher;
|
||||||
|
import org.springframework.util.PathMatcher;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态权限数据源,用于获取动态权限规则
|
||||||
|
* Created by macro on 2020/2/7.
|
||||||
|
*/
|
||||||
|
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
|
||||||
|
|
||||||
|
private static Map<String, ConfigAttribute> configAttributeMap = null;
|
||||||
|
@Autowired
|
||||||
|
private DynamicSecurityService dynamicSecurityService;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void loadDataSource() {
|
||||||
|
configAttributeMap = dynamicSecurityService.loadDataSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearDataSource() {
|
||||||
|
configAttributeMap.clear();
|
||||||
|
configAttributeMap = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
|
||||||
|
if (configAttributeMap == null) this.loadDataSource();
|
||||||
|
List<ConfigAttribute> configAttributes = new ArrayList<>();
|
||||||
|
//获取当前访问的路径
|
||||||
|
String url = ((FilterInvocation) o).getRequestUrl();
|
||||||
|
String path = URLUtil.getPath(url);
|
||||||
|
PathMatcher pathMatcher = new AntPathMatcher();
|
||||||
|
Iterator<String> iterator = configAttributeMap.keySet().iterator();
|
||||||
|
//获取访问该路径所需资源
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
String pattern = iterator.next();
|
||||||
|
if (pathMatcher.match(pattern, path)) {
|
||||||
|
configAttributes.add(configAttributeMap.get(pattern));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 未设置操作请求权限,返回空集合
|
||||||
|
return configAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<ConfigAttribute> getAllConfigAttributes() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supports(Class<?> aClass) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.macro.mall.security.component;
|
||||||
|
|
||||||
|
import org.springframework.security.access.ConfigAttribute;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态权限相关业务类
|
||||||
|
* Created by macro on 2020/2/7.
|
||||||
|
*/
|
||||||
|
public interface DynamicSecurityService {
|
||||||
|
/**
|
||||||
|
* 加载资源ANT通配符和资源对应MAP
|
||||||
|
*/
|
||||||
|
Map<String, ConfigAttribute> loadDataSource();
|
||||||
|
}
|
@ -20,6 +20,8 @@ public class RestfulAccessDeniedHandler implements AccessDeniedHandler{
|
|||||||
public void handle(HttpServletRequest request,
|
public void handle(HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
AccessDeniedException e) throws IOException, ServletException {
|
AccessDeniedException e) throws IOException, ServletException {
|
||||||
|
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
response.setHeader("Cache-Control","no-cache");
|
||||||
response.setCharacterEncoding("UTF-8");
|
response.setCharacterEncoding("UTF-8");
|
||||||
response.setContentType("application/json");
|
response.setContentType("application/json");
|
||||||
response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage())));
|
response.getWriter().println(JSONUtil.parse(CommonResult.forbidden(e.getMessage())));
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package com.macro.mall.security.config;
|
package com.macro.mall.security.config;
|
||||||
|
|
||||||
import com.macro.mall.security.component.JwtAuthenticationTokenFilter;
|
import com.macro.mall.security.component.*;
|
||||||
import com.macro.mall.security.component.RestAuthenticationEntryPoint;
|
|
||||||
import com.macro.mall.security.component.RestfulAccessDeniedHandler;
|
|
||||||
import com.macro.mall.security.util.JwtTokenUtil;
|
import com.macro.mall.security.util.JwtTokenUtil;
|
||||||
|
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.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
@ -14,6 +14,7 @@ import org.springframework.security.config.annotation.web.configurers.Expression
|
|||||||
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.bcrypt.BCryptPasswordEncoder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
|
||||||
@ -23,6 +24,9 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
|
|||||||
*/
|
*/
|
||||||
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private DynamicSecurityService dynamicSecurityService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configure(HttpSecurity httpSecurity) throws Exception {
|
protected void configure(HttpSecurity httpSecurity) throws Exception {
|
||||||
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
|
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
|
||||||
@ -53,6 +57,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
// 自定义权限拦截器JWT过滤器
|
// 自定义权限拦截器JWT过滤器
|
||||||
.and()
|
.and()
|
||||||
.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
|
.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
|
||||||
|
//有动态权限配置时添加动态权限校验过滤器
|
||||||
|
if(dynamicSecurityService!=null){
|
||||||
|
registry.and().addFilterBefore(dynamicSecurityFilter(), FilterSecurityInterceptor.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -97,4 +105,23 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
|
|||||||
return new JwtTokenUtil();
|
return new JwtTokenUtil();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ConditionalOnBean(name = "dynamicSecurityService")
|
||||||
|
@Bean
|
||||||
|
public DynamicAccessDecisionManager dynamicAccessDecisionManager() {
|
||||||
|
return new DynamicAccessDecisionManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ConditionalOnBean(name = "dynamicSecurityService")
|
||||||
|
@Bean
|
||||||
|
public DynamicSecurityFilter dynamicSecurityFilter() {
|
||||||
|
return new DynamicSecurityFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConditionalOnBean(name = "dynamicSecurityService")
|
||||||
|
@Bean
|
||||||
|
public DynamicSecurityMetadataSource dynamicSecurityMetadataSource() {
|
||||||
|
return new DynamicSecurityMetadataSource();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user