springboot+springsecurity如何实现动态url细粒度权限认证

网友投稿 464 2022-10-18


springboot+springsecurity如何实现动态url细粒度权限认证

谨记:Url表只储存受保护的资源,不在表里的资源说明不受保护,任何人都可以访问

1、MyFilterInvocationSecurityMetadataSource 类判断该访问路径是否被保护

@Component

//用于设置受保护资源的权限信息的数据源

public class MyFilterInvocationSecurityMetadataSource implements

FilterInvocationSecurityMetadataSource {

@Bean

public AntPathMatcher getAntPathMatcher(){

return new AntPathMatcher();

}

@Autowired

//获取数据库中的保存的url Url表只储存受保护的资源,不在表里的资源说明不受保护,任何人都可以访问

private RightsMapper rightsMapper;

@Autowired

private AntPathMatcher antPathMatcher;

@Override

/*

* @param 被调用的保护资源

* @return 返回能够访问该保护资源的角色集合,如果没有,则应返回空集合。

*/

public Collection getAttributes(Object object)

throws IllegalArgumentException {

FilterInvocation fi = (FilterInvocation) object;

//获取用户请求的Url

String url = fi.getRequestUrl();

//先到数据库获取受权限控制的Url

List us = rightsMapper.queryAll();

//用于储存用户请求的Url能够访问的角色

Collection rs=new ArrayList();

for(Rights u:us){

if (u.getUrl() != null) {

//逐一判断用户请求的Url是否和数据库中受权限控制的Url有匹配的

if (antPathMatcher.match(u.getUrl(), url)) {

//如果有则将可以访问该Url的角色储存到Collection

rs.add(rightsMapper.queryById(u.getId()));

}

}

}

if(rs.size()>0) {

return rs;

}

//没有匹配到,就说明此资源没有被控制,所有人都可以访问,返回null即可,返回null则不会进入之后的decide方法

return null;

}

@Override

public Collection getAllConfigAttributes() {

// TODO 自动生成的方法存根

return nuhttp://ll;

}

@Override

public boolean supports(Class> clazz) {

// TODO 自动生成的方法存根

return FilterInvocation.class.isAssignableFrom(clazz);

}

}

rights表中的部分内容:

表结构

内容:

2、MyAccessDecisionManager 类判断该用户是否有权限访问

@Component

//用于设置判断当前用户是否可以访问被保护资源的逻辑

public class MyAccessDecisionManager implements AccessDecisionManager {

@Override

/*

* @param 请求该保护资源的用户对象

* @param 被调用的保护资源

* @param 有权限调用该资源的集合

*/

public void decide(Authentication authentication, Object object,

Collection configAttributes)

throws AccessDeniedException, InsufficientAuthenticationException {

Iterator ite = configAttributes.iterator();

//遍历configAttributes,查看当前用户是否有对应的权限访问该保护资源

while (ite.hasNext()) {

ConfigAttribute ca = ite.next();

String needRole = ca.getAttribute();

for (GrantedAuthority ga : authentication.getAuthorities()) {

if (ga.getAuthority().equals(needRole)) {

// 匹配到有对应角色,则允许通过

return;

}

}

}

// 该url有配置权限,但是当前登录用户没有匹配到对应权限,则禁止访问

throw new AccessDeniedException("not allow");

}

@Override

public boolean supports(ConfigAttribute attribute) {

return true;

}

@Override

public boolean supports(Class> clazz) {

return true;

}

}

3、在SecurityConfig 类中配置说明

@EnableWebSecurity

@EnableGlobalMethodSecurity(prePostEnabled = true)

public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired

MyUserDetailsService myUserDetailsService;

@Autowired

private SendSmsSecurityConfig sendSmsSecurityConfig;

@Autowired

private MyAccessDecisionManager myAccessDecisionManager;

@Autowired

private MyFilterInvocationSecurityMetadataSource myFilterInvocationSecurityMetadataSource;

//加密机制

@Bean

public PasswordEncoder passwordEncoder() {

return NoOpPasswordEncoder.getInstance(); // 不加密

}

//认证

@Override

protected void configure(AuthenticationManagerBuilder auth) throws Exception {

auth.userDetailsService(myUserDetailsService)

.passwordEncoder(passwordEncoder());http://

}

@Override

protected void configure(HttpSecurity http) throws Exception {

http.authorizeRequests()//对请求授权

.antMatchers("/**").permitAll()

.anyRequest()//任何请求

.authenticated()//登录后访问

.withObjectPostProcessor(

new ObjectPostProcessor() {

@Override

public O postProcess(

O fsi) {

fsi.setSecurityMetadataSource(myFilterInvocationSecurityMetadataSource);

fsi.setAccessDecisionManager(myAccessDecisionManager);

return fsi;

}

})

.and().csrf().disable();

}

}

配置如下代码:

至此完成所有配置!!!

SpringSecurity解决公共接口自定义权限验证失效问题,和源码分析

背景:

自定义权限认证,一部分接口必须要有相应的角色权限,一部分接口面向所有访问者,一部分接口任何人都不能访问。但是在使用 SpringSecurity的过程中发现,框架会将没有指定角色列表的URL资源直接放行,不做拦截。

用户登录认证成功后,携带Token访问URL资源,spring security 根据Token(请求头Authorization中)来分辨不同用户。

用户权限数据源是一个Map:以 URL资源为Key,以有权访问的Key的角色列表为Value。

使用时发现当一个接口有Key,但是Value为空或null时,spring security 框架自动放行,导致了权限失效问题。

解决方法有两种:

第一种方法:

默认rejectPublicInvocations为false。

对需要控制权限的URL资源添加标志,以防止roleList为空,跳过了权限验证.

公共权限设置为null,不进行权限验证

第二种方法:

配置rejectPublicInvocations为true

此后roleList为空,或者没有找到URL资源时,都为拒绝访问

需要控制权限的URL资源,即使对应角色为空,也会进行权限验证

公共权限设置为所有角色和匿名角色,不进行权限验证

package org.springframework.security.access.intercept;

/**

* 对安全对象(访问请求+用户主体)拦截的抽象类源码

*/

public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {

// ... 其他方法省略

protected InterceptorStatusToken beforeInvocation(Object object) {

Assert.notNull(object, "Object was null");

final boolean debug = logger.isDebugEnabled();

if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {

throw new IllegalArgumentException(

"Security invocation attempted for object "

+ object.getClass().getName()

+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "

+ getSecureObjectClass());

}

// 从权限数据源获取了当前 对应的 <角色列表>

Collection attributes = this.obtainSecurityMetadataSource().getAttributes(object);

// 框架在此处判断URL资源对应的角色列表是否为空

if (attributes == null || attributehttp://s.isEmpty()) {

// rejectPublicInvocations默认为false

// 可以配置为true,即角色列表为空的时候不进行放行

if (rejectPublicInvocations) {

throw new IllegalArgumentException(

"Secure object invocation "

+ object

+ " was denied as public invocations are not allowed via this interceptor. "

+ "This indicates a configuration error because the "

+ "rejectPublicInvocations property is set to 'true'");

}

if (debug) {

logger.debug("Public object - authentication not attempted");

}

publishEvent(new PublicInvocationEvent(object));

return null; // no further work post-invocation

}

if (debug) {

logger.debug("Secure object: " + object + "; Attributes: " + attributes);

}

// 如果当前用户权限对象为null

if (SecurityContextHolder.getContext().getAuthentication() == null) {

credentialsNotFound(messages.getMessage(

"AbstractSecurityInterceptor.authenticationNotFound",

"An Authentication object was not found in the SecurityContext"),

object, attributes);

}

Authentication authenticated = authenticateIfRequired();

// Attempt authorization,此处调用accessDecisionManager 进行鉴权

try {

this.accessDecisionManager.decide(authenticated, object, attributes);

}

catch (AccessDeniedException accessDehttp://niedException) {

publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,

accessDeniedException));

throw accessDeniedException;

}

if (debug) {

logger.debug("Authorization successful");

}

if (publishAuthorizationSuccess) {

publishEvent(new AuthorizedEvent(object, attributes, authenticated));

}

// Attempt to run as a different user,这里可以另外配置或修改用户的权限对象,特殊场景使用

Authentication runAs = this.runAsManager.buildRunAs(authenticated, object,

attributes);

if (runAs == null) {

if (debug) {

logger.debug("RunAsManager did not change Authentication object");

}

// no further work post-invocation

return new InterceptorStatusToken(SecurityContextHolder.getContext(), false,

attributes, object);

}

else {

if (debug) {

logger.debug("Switching to RunAs Authentication: " + runAs);

}

SecurityContext origCtx = SecurityContextHolder.getContext();

SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());

SecurityContextHolder.getContext().setAuthentication(runAs);

// need to revert to token.Authenticated post-invocation

return new InterceptorStatusToken(origCtx, true, attributes, object);

}

}

// ... 其他方法略

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:电能管理系统的设计与应用
下一篇:湖为底·数共生·谋未来,纳多德参加易华录2021第三届数据湖大会
相关文章

 发表评论

暂时没有评论,来抢沙发吧~