Spring Security OAuth2+JWT(动态权限)
前言
有好多同学再问我 Security 鉴权方法的问题。在这里也给出一点思路。
权限校验思路
- Gateway校验token
- security注解控制权限
- 采用自定义FilterInvocationSecurityMetadataSource的方式来实现动态权限
Gateway校验token
这种方式就是自己去解析token认证权限 在之前的文章中有提到过
security注解控制权限
这种方式也是网上教程最多的 但是不符合我们的需求(我们需要从数据库中读取相对于的权限信息)
自定义FilterInvocationSecurityMetadataSource(推荐)
这个方式是对Security鉴权的拓展 同时也满足我们的需求
FilterInvocationSecurityMetadataSource
- 重点就是getAttributes方法 其实返回的就是一个角色的集合
- configAttributeMap 就是我们从数据库中取出来的权限
public class SecurityOauthMetadataSource implements FilterInvocationSecurityMetadataSource {
private FilterInvocationSecurityMetadataSource superMetadataSource;
private static SecurityOauthService securityOauthService;
private static Map<String, List<ConfigAttribute>> configAttributeMap = null;
private final AntPathMatcher matcher = new AntPathMatcher(File.separator);
public SecurityOauthMetadataSource(FilterInvocationSecurityMetadataSource expressionBasedFilterInvocationSecurityMetadataSource,SecurityOauthService securityOauthService){
this.superMetadataSource = expressionBasedFilterInvocationSecurityMetadataSource;
this.securityOauthService = securityOauthService;
}
public static void loadDataSource() {
if(configAttributeMap == null){
configAttributeMap = securityOauthService.loadDataSource();
}
}
public void clearDataSource() {
configAttributeMap.clear();
configAttributeMap = null;
}
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
loadDataSource();
FilterInvocation fi = (FilterInvocation) o;
String url = fi.getRequestUrl();
//System.out.println(fi.getRequest().getMethod());
String path = url.split("\\?")[0];
List<ConfigAttribute> configAttributes = new ArrayList<>();
//AntPathMatcher matcher = new AntPathMatcher(File.separator);
if(matcher.match("/public/**",path)|| path.equals("/file/getImage")
|| fi.getRequest().getMethod().equals("OPTIONS")
||matcher.match("/swagger-ui.html",path)
||matcher.match("/webjars/**",path)
||matcher.match("/v2/**",path)
||matcher.match("/swagger-resources",path)
||matcher.match("/swagger-resources/**",path)
){
return SecurityConfig.createList("ROLE_ANONYMOUS","ROLE_ADMIN","ROLE_CLIENT");
}
if(configAttributeMap.get(path) != null){
return configAttributeMap.get(path);
}else{
return superMetadataSource.getAttributes(o);
}
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
AccessDecisionManager
将当前登录的用户所拥护的角色和该资源所需要的的角色权限做比较
public class SecurityOauthAccessDecisionManager implements AccessDecisionManager {
@Override
public void decide(Authentication authentication, Object object,
Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
Iterator<ConfigAttribute> iterator = configAttributes.iterator();
while (iterator.hasNext()) {
ConfigAttribute configAttribute = iterator.next();
//将访问所需资源或用户拥有资源进行比对
String needAuthority = configAttribute.getAttribute();
if(needAuthority == null){
//return ;
needAuthority = "ROLE_ADMIN";
}
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;
}
}
SecurityOauthService
从数据库读取权限信息
@Service
public class SecurityOauthServiceImpl implements SecurityOauthService {
@Override
public Map<String, List<ConfigAttribute>> loadDataSource() {
//从数据库读取配置
Map<String, List<ConfigAttribute>> urlRoleMap = new HashMap<String,List<ConfigAttribute>>(){{
//put("/ddptYyz/getDdptYyzAll","ROLE_ADMIN");
put("/user/test2", SecurityConfig.createList("ROLE_ADMIN","ROLE_USER1"));
put("/user/test3",SecurityConfig.createList("ROLE_ADMIN","ROLE_USER1"));
}};
return urlRoleMap;
}
}
配置ResourceServerConfigurerAdapter
@Order(3)
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private SecurityOauthService securityOauthService;
@Override
public void configure(HttpSecurity http) throws Exception {
http
// Since we want the protected resources to be accessible in the UI as well we need
// session creation to be allowed (it's disabled by default in 2.0.6)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/oauth/**","/public/login").permitAll()
.and().authorizeRequests().anyRequest().authenticated().and()
.authorizeRequests()
// 自定义FilterInvocationSecurityMetadataSource
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(
O fsi) {
fsi.setSecurityMetadataSource(new SecurityOauthMetadataSource(fsi.getSecurityMetadataSource(),securityOauthService));
fsi.setAccessDecisionManager(securityOauthAccessDecisionManager());
return fsi;
}
}).and().csrf().disable();
}
@Bean
public SecurityOauthAccessDecisionManager securityOauthAccessDecisionManager(){
return new SecurityOauthAccessDecisionManager();
}
}