SpringBootはSpringSecurityを統合して、動的なインターフェイス管理権限を実装します
前回の記事に続き、パーミッション管理はバックグラウンド管理に欠かせない要素です。現在、Spring Securityと組み合わせて、インターフェースの動的管理を実装しています。
動的な権利管理
Spring Securityは、権限の動的管理を実装します。最初のステップは、フィルターを作成することです。doFilterメソッドは、OPTIONSに直接注意を払う必要があります。そうしないと、クロスドメインの問題が発生します。また、前の記事で説明したIgnoreUrlsConfigのホワイトリストも直接リリースされ、すべてのアクセス許可操作はsuper.beforeInvocation(fi)に実装されます。
/**
* 动态权限过滤器,用于实现基于路径的动态权限过滤
*
*/
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;
}
}
DynamicSecurityFilterでsuper.beforeInvocation(fi)メソッドが呼び出されると、AccessDecisionManagerのdecideメソッドが認証操作のために呼び出され、decideメソッドのconfigAttributesパラメーターはSecurityMetadataSourceのgetAttributesメソッドを介して取得されます。configAttributesは実際には現在のインターフェイスに必要な権限。以下は、beforeInvocationソースコードの簡略版です。
public abstract class AbstractSecurityInterceptor implements InitializingBean,
ApplicationEventPublisherAware, MessageSourceAware {
protected InterceptorStatusToken beforeInvocation(Object object) {
//获取元数据
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
Authentication authenticated = authenticateIfRequired();
//进行鉴权操作
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
}
catch (AccessDeniedException accessDeniedException) {
publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
accessDeniedException));
throw accessDeniedException;
}
}
}
上記の紹介では、次に、SecurityMetadataSourceインターフェイスのgetAttributesメソッドを実装して、現在アクセスされているパスリソースを取得します。
/**
* 动态权限数据源,用于获取动态权限规则
*
*/
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;
}
}
バックグラウンドリソースは、ルールによってMAPオブジェクトにキャッシュされます。バックグラウンドリソースが変更された場合、キャッシュをクリアして、次のクエリで再ロードする必要があります。MyMesResourceControllerを変更してDynamicSecurityMetadataSourceを挿入する必要があります。バックグラウンドリソースを変更するときは、clearDataSourceメソッドを呼び出してキャッシュされたデータをクリアする必要があります。
/**
* 后台资源管理Controller
*
*/
@Controller
@Api(tags = "MyMesResourceController", description = "后台资源管理")
@RequestMapping("/resource")
public class MyMesResourceController {
@Autowired
private MyMesResourceService resourceService;
@Autowired
private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;
@ApiOperation("添加后台资源")
@RequestMapping(value = "/create", method = RequestMethod.POST)
@ResponseBody
public CommonResult create(@RequestBody UmsResource umsResource) {
int count = resourceService.create(umsResource);
dynamicSecurityMetadataSource.clearDataSource();
if (count > 0) {
return CommonResult.success(count);
} else {
return CommonResult.failed();
}
}
}
アクセス許可の検証を行うには、AccessDecisionManagerインターフェイスを実装する必要があります。リソースが設定されていないインターフェイスの場合は直接アクセスを許可します。リソースが設定されているインターフェイスの場合は、アクセスに必要なリソースをユーザーが所有するリソースと比較し、一致する場合はアクセスを許可します。
/**
* 动态权限决策管理器,用于判断用户是否有访问权限
*
*/
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;
}
}
以前、DynamicSecurityServiceオブジェクトをDynamicSecurityMetadataSourceに挿入しました。これは、カスタマイズした動的アクセス許可ビジネスインターフェイスであり、主にすべてのバックグラウンドリソースルールをロードするために使用されます。
/**
* 动态权限相关业务类
*
*/
public interface DynamicSecurityService {
/**
* 加载资源ANT通配符和资源对应MAP
*/
Map<String, ConfigAttribute> loadDataSource();
}
SpringSecurityと組み合わせたインターフェースの動的管理権限は基本的に実現されています。明日Redis + AOP最適化権限管理について説明します。
公共
公式アカウントhttps://mp.weixin.qq.com/s/nfat2WWWUXdmfUGFBAVEuA