@@ -1,11 +1,8 @@ | |||||
package com.xkcoding.rbac.security.config; | package com.xkcoding.rbac.security.config; | ||||
import com.google.common.collect.Lists; | |||||
import lombok.Data; | import lombok.Data; | ||||
import org.springframework.boot.context.properties.ConfigurationProperties; | import org.springframework.boot.context.properties.ConfigurationProperties; | ||||
import java.util.List; | |||||
/** | /** | ||||
* <p> | * <p> | ||||
* 自定义配置 | * 自定义配置 | ||||
@@ -25,5 +22,5 @@ public class CustomConfig { | |||||
/** | /** | ||||
* 不需要拦截的地址 | * 不需要拦截的地址 | ||||
*/ | */ | ||||
private List<String> ignores = Lists.newArrayList(); | |||||
private IgnoreConfig ignores; | |||||
} | } |
@@ -0,0 +1,67 @@ | |||||
package com.xkcoding.rbac.security.config; | |||||
import com.google.common.collect.Lists; | |||||
import lombok.Data; | |||||
import java.util.List; | |||||
/** | |||||
* <p> | |||||
* 忽略配置 | |||||
* </p> | |||||
* | |||||
* @package: com.xkcoding.rbac.security.config | |||||
* @description: 忽略配置 | |||||
* @author: yangkai.shen | |||||
* @date: Created in 2018-12-17 17:37 | |||||
* @copyright: Copyright (c) 2018 | |||||
* @version: V1.0 | |||||
* @modified: yangkai.shen | |||||
*/ | |||||
@Data | |||||
public class IgnoreConfig { | |||||
/** | |||||
* 需要忽略的 URL 格式,不考虑请求方法 | |||||
*/ | |||||
private List<String> pattern = Lists.newArrayList(); | |||||
/** | |||||
* 需要忽略的 GET 请求 | |||||
*/ | |||||
private List<String> get = Lists.newArrayList(); | |||||
/** | |||||
* 需要忽略的 POST 请求 | |||||
*/ | |||||
private List<String> post = Lists.newArrayList(); | |||||
/** | |||||
* 需要忽略的 DELETE 请求 | |||||
*/ | |||||
private List<String> delete = Lists.newArrayList(); | |||||
/** | |||||
* 需要忽略的 PUT 请求 | |||||
*/ | |||||
private List<String> put = Lists.newArrayList(); | |||||
/** | |||||
* 需要忽略的 HEAD 请求 | |||||
*/ | |||||
private List<String> head = Lists.newArrayList(); | |||||
/** | |||||
* 需要忽略的 PATCH 请求 | |||||
*/ | |||||
private List<String> patch = Lists.newArrayList(); | |||||
/** | |||||
* 需要忽略的 OPTIONS 请求 | |||||
*/ | |||||
private List<String> options = Lists.newArrayList(); | |||||
/** | |||||
* 需要忽略的 TRACE 请求 | |||||
*/ | |||||
private List<String> trace = Lists.newArrayList(); | |||||
} |
@@ -1,7 +1,9 @@ | |||||
package com.xkcoding.rbac.security.config; | package com.xkcoding.rbac.security.config; | ||||
import cn.hutool.core.collection.CollUtil; | import cn.hutool.core.collection.CollUtil; | ||||
import cn.hutool.core.util.ObjectUtil; | |||||
import cn.hutool.core.util.StrUtil; | import cn.hutool.core.util.StrUtil; | ||||
import com.google.common.collect.Sets; | |||||
import com.xkcoding.rbac.security.common.Status; | import com.xkcoding.rbac.security.common.Status; | ||||
import com.xkcoding.rbac.security.exception.SecurityException; | import com.xkcoding.rbac.security.exception.SecurityException; | ||||
import com.xkcoding.rbac.security.service.CustomUserDetailsService; | import com.xkcoding.rbac.security.service.CustomUserDetailsService; | ||||
@@ -9,6 +11,7 @@ import com.xkcoding.rbac.security.util.JwtUtil; | |||||
import com.xkcoding.rbac.security.util.ResponseUtil; | import com.xkcoding.rbac.security.util.ResponseUtil; | ||||
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.http.HttpMethod; | |||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; | ||||
import org.springframework.security.core.context.SecurityContextHolder; | import org.springframework.security.core.context.SecurityContextHolder; | ||||
import org.springframework.security.core.userdetails.UserDetails; | import org.springframework.security.core.userdetails.UserDetails; | ||||
@@ -22,6 +25,7 @@ import javax.servlet.ServletException; | |||||
import javax.servlet.http.HttpServletRequest; | import javax.servlet.http.HttpServletRequest; | ||||
import javax.servlet.http.HttpServletResponse; | import javax.servlet.http.HttpServletResponse; | ||||
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.Set; | |||||
/** | /** | ||||
* <p> | * <p> | ||||
@@ -50,14 +54,10 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { | |||||
@Override | @Override | ||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { | protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { | ||||
if (CollUtil.isNotEmpty(customConfig.getIgnores())) { | |||||
for (String ignore : customConfig.getIgnores()) { | |||||
AntPathRequestMatcher matcher = new AntPathRequestMatcher(ignore); | |||||
if (matcher.matches(request)) { | |||||
filterChain.doFilter(request, response); | |||||
return; | |||||
} | |||||
} | |||||
if (checkIgnores(request)) { | |||||
filterChain.doFilter(request, response); | |||||
return; | |||||
} | } | ||||
String jwt = jwtUtil.getJwtFromRequest(request); | String jwt = jwtUtil.getJwtFromRequest(request); | ||||
@@ -82,4 +82,72 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { | |||||
} | } | ||||
/** | |||||
* 请求是否不需要进行权限拦截 | |||||
* | |||||
* @param request 当前请求 | |||||
* @return true - 忽略,false - 不忽略 | |||||
*/ | |||||
private boolean checkIgnores(HttpServletRequest request) { | |||||
String method = request.getMethod(); | |||||
HttpMethod httpMethod = HttpMethod.resolve(method); | |||||
if (ObjectUtil.isNull(httpMethod)) { | |||||
httpMethod = HttpMethod.GET; | |||||
} | |||||
Set<String> ignores = Sets.newHashSet(); | |||||
switch (httpMethod) { | |||||
case GET: | |||||
ignores.addAll(customConfig.getIgnores() | |||||
.getGet()); | |||||
break; | |||||
case PUT: | |||||
ignores.addAll(customConfig.getIgnores() | |||||
.getPut()); | |||||
break; | |||||
case HEAD: | |||||
ignores.addAll(customConfig.getIgnores() | |||||
.getHead()); | |||||
break; | |||||
case POST: | |||||
ignores.addAll(customConfig.getIgnores() | |||||
.getPost()); | |||||
break; | |||||
case PATCH: | |||||
ignores.addAll(customConfig.getIgnores() | |||||
.getPatch()); | |||||
break; | |||||
case TRACE: | |||||
ignores.addAll(customConfig.getIgnores() | |||||
.getTrace()); | |||||
break; | |||||
case DELETE: | |||||
ignores.addAll(customConfig.getIgnores() | |||||
.getDelete()); | |||||
break; | |||||
case OPTIONS: | |||||
ignores.addAll(customConfig.getIgnores() | |||||
.getOptions()); | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
ignores.addAll(customConfig.getIgnores() | |||||
.getPattern()); | |||||
if (CollUtil.isNotEmpty(ignores)) { | |||||
for (String ignore : ignores) { | |||||
AntPathRequestMatcher matcher = new AntPathRequestMatcher(ignore, method); | |||||
if (matcher.matches(request)) { | |||||
return true; | |||||
} | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
} | } |
@@ -1,12 +1,11 @@ | |||||
package com.xkcoding.rbac.security.config; | package com.xkcoding.rbac.security.config; | ||||
import cn.hutool.core.collection.CollUtil; | |||||
import cn.hutool.core.util.ArrayUtil; | |||||
import com.xkcoding.rbac.security.service.CustomUserDetailsService; | import com.xkcoding.rbac.security.service.CustomUserDetailsService; | ||||
import org.springframework.beans.factory.annotation.Autowired; | import org.springframework.beans.factory.annotation.Autowired; | ||||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | import org.springframework.boot.context.properties.EnableConfigurationProperties; | ||||
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.http.HttpMethod; | |||||
import org.springframework.security.authentication.AuthenticationManager; | import org.springframework.security.authentication.AuthenticationManager; | ||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; | ||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||||
@@ -81,9 +80,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { | |||||
// 认证请求 | // 认证请求 | ||||
.authorizeRequests() | .authorizeRequests() | ||||
// 放行 /api/auth/** 的所有请求,参见 AuthController | |||||
//.antMatchers("/api/auth/**") | |||||
//.permitAll() | |||||
// 所有请求都需要登录访问 | |||||
.anyRequest() | .anyRequest() | ||||
.authenticated() | .authenticated() | ||||
// RBAC 动态 url 认证 | // RBAC 动态 url 认证 | ||||
@@ -109,11 +106,69 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { | |||||
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); | http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); | ||||
} | } | ||||
/** | |||||
* 放行所有不需要登录就可以访问的请求,参见 AuthController | |||||
* 也可以在 {@link #configure(HttpSecurity)} 中配置 | |||||
* {@code http.authorizeRequests().antMatchers("/api/auth/**").permitAll()} | |||||
*/ | |||||
@Override | @Override | ||||
public void configure(WebSecurity web) throws Exception { | |||||
if (CollUtil.isNotEmpty(customConfig.getIgnores())) { | |||||
web.ignoring() | |||||
.antMatchers(ArrayUtil.toArray(customConfig.getIgnores(), String.class)); | |||||
} | |||||
public void configure(WebSecurity web) { | |||||
WebSecurity and = web.ignoring() | |||||
.and(); | |||||
// 忽略 GET | |||||
customConfig.getIgnores() | |||||
.getGet() | |||||
.forEach(url -> and.ignoring() | |||||
.antMatchers(HttpMethod.GET, url)); | |||||
// 忽略 POST | |||||
customConfig.getIgnores() | |||||
.getPost() | |||||
.forEach(url -> and.ignoring() | |||||
.antMatchers(HttpMethod.POST, url)); | |||||
// 忽略 DELETE | |||||
customConfig.getIgnores() | |||||
.getDelete() | |||||
.forEach(url -> and.ignoring() | |||||
.antMatchers(HttpMethod.DELETE, url)); | |||||
// 忽略 PUT | |||||
customConfig.getIgnores() | |||||
.getPut() | |||||
.forEach(url -> and.ignoring() | |||||
.antMatchers(HttpMethod.PUT, url)); | |||||
// 忽略 HEAD | |||||
customConfig.getIgnores() | |||||
.getHead() | |||||
.forEach(url -> and.ignoring() | |||||
.antMatchers(HttpMethod.HEAD, url)); | |||||
// 忽略 PATCH | |||||
customConfig.getIgnores() | |||||
.getPatch() | |||||
.forEach(url -> and.ignoring() | |||||
.antMatchers(HttpMethod.PATCH, url)); | |||||
// 忽略 OPTIONS | |||||
customConfig.getIgnores() | |||||
.getOptions() | |||||
.forEach(url -> and.ignoring() | |||||
.antMatchers(HttpMethod.OPTIONS, url)); | |||||
// 忽略 TRACE | |||||
customConfig.getIgnores() | |||||
.getTrace() | |||||
.forEach(url -> and.ignoring() | |||||
.antMatchers(HttpMethod.TRACE, url)); | |||||
// 按照请求格式忽略 | |||||
customConfig.getIgnores() | |||||
.getPattern() | |||||
.forEach(url -> and.ignoring() | |||||
.antMatchers(url)); | |||||
} | } | ||||
} | } |
@@ -50,5 +50,10 @@ logging: | |||||
custom: | custom: | ||||
config: | config: | ||||
ignores: | ignores: | ||||
- "/api/auth/login" | |||||
- "/api/auth/logout" | |||||
# 需要过滤的 post 请求 | |||||
post: | |||||
- "/api/auth/login" | |||||
- "/api/auth/logout" | |||||
# 需要过滤的请求,不限方法 | |||||
pattern: | |||||
- "/test/*" |