补充上文的根据URI获取权限判断的优化,上文的方法只能根据uri来获取单个权限,无法满足uri可以根据多个或并且的方式来访问的请求。
优化内容:先增加一级获取uri然后判断uri需要什么权限,可以多个并且的权限 等等然后 在经过权限判断器进行拦截判断
新建自定义的url权限判断MyFilterInvocationSecurityMetadataSource 实现FilterInvocationSecurityMetadataSource
/**
* @Description 根据url获取 url需要访问的权限
* @Author wwz
* @Date 2019/08/01
* @Param
* @Return
*/
@Component
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private AntPathMatcher antPathMatcher = new AntPathMatcher(); // 模糊匹配 如何 auth/** auth/auth
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
Set<ConfigAttribute> set = new HashSet<>();
String requestUrl = ((FilterInvocation) object).getRequest().getMethod() + ((FilterInvocation) object).getRequest().getRequestURI();
System.out.println("requestUrl >> " + requestUrl);
// 这里获取对比数据可以从数据库或者内存 redis等等地方获取 目前先写死后面优化
String url = "GET/auth/**";
if (antPathMatcher.match(url, requestUrl)) {
SecurityConfig securityConfig = new SecurityConfig("ROLE_ADMIN");
set.add(securityConfig);
}
if (ObjectUtils.isEmpty(set)) {
return SecurityConfig.createList("ROLE_LOGIN");
}
return set;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
修改原来MySecurityAccessDecisionManager 的decide,从获取到url 改成获取到需要什么权限。其他判断不变
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
System.out.println("collection>>" + configAttributes);
for (ConfigAttribute configAttribute : configAttributes) {
// 当前请求需要的权限
String needRole = configAttribute.getAttribute();
// 当前用户所具有的权限
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
System.out.println("authorities=" + authorities);
for (GrantedAuthority grantedAuthority : authorities) {
if (grantedAuthority.getAuthority().equals(needRole)) {
return;
}
if (grantedAuthority.getAuthority().equals("ROLE_ADMIN")) {
return;
}
}
}
throw new AccessDeniedException("无访问权限");
}
把MyFilterInvocationSecurityMetadataSource 注册到MySecurityResourceServerConfig的重写方法
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling().authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 另外,如果不设置,那么在通过浏览器访问被保护的任何资源时,每次是不同的SessionID,并且将每次请求的历史都记录在OAuth2Authentication的details的中
.and()
.authorizeRequests().antMatchers("/actuator/health").permitAll().anyRequest().authenticated() // httpSecurity 放过健康检查,其他都需要验证 设置了.anyRequest().authenticated()才回进入自定义的权限判断
.and()
.requestMatchers().antMatchers("/auth/**") // .requestMatchers().antMatchers(...) OAuth2设置对资源的保护如果是用 /**的话 会把上面的也拦截掉
.and()
.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { // 重写做权限判断
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setSecurityMetadataSource(filterInvocationSecurityMetadataSource); // 请求需要权限
o.setAccessDecisionManager(accessDecisionManager); // 权限判断
return o;
}
})
.and()
.httpBasic();
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
尝试并请求打印信息: