Spring Security 控制授权

Spring Security 控制授权基本原理

Spring Security 在安全认证的过程中,通过一系列的过滤器一层层对登录用户的身份进行认证。而关于权限认证的过滤器则在整个过滤器链的最后,是Spring Security整个认证过程的最后一道关卡,这个过滤器就是 FilterSecurityInterceptor
在这里插入图片描述

控制授权流程源码解析

服务请求被 FilterSecurityInterceptor拦截后,

  1. 加载SecurityConfig安全配置,比如在HttpSecurity中的配置信息,
  2. 通过SecurityContextHolder加载登录用户的个人信息。
  3. 把安全配置信息,用户个人信息,登录请求信息交给AccessDecisionManager进行处理
  4. AccessDecisionManager调用AccessDecisionVoter进行投票处理,只要一票未过,则抛出异常,认证未通过
  5. 认证通过,则进行接口调用
    在这里插入图片描述

权限表达式

Spring Security通过权限表达式进行权限配置
在这里插入图片描述

权限模块实现思路

  • AuthorizeConfigProvider:授权配置提供器,各个模块和业务系统可以通过实现此接口向系统添加授权配置。
  • AuthorizeConfigManager:授权信息管理器,用于收集系统中所有 AuthorizeConfigProvider 并加载其配置
    实现思路
    权限模块一般是基于RBAC(Role-Based Access Control )模型进行设计。每个模块可以通过实现AuthorizeConfigProvider接口自定义权限验证逻辑,最后交由 AuthorizeConfigManager实现类进行授权配置管理。每个应用通过实现权限模块和业务模块完成权限认证。
    在这里插入图片描述

实现代码

/**
 * 授权配置提供器,各个模块和业务系统可以通过实现此接口向系统添加授权配置。
 * 
 */
public interface AuthorizeConfigProvider {
	
	/**
	 * @param config
	 * @return 返回的boolean表示配置中是否有针对anyRequest的配置。在整个授权配置中,
	 * 应该有且仅有一个针对anyRequest的配置,如果所有的实现都没有针对anyRequest的配置,
	 * 系统会自动增加一个anyRequest().authenticated()的配置。如果有多个针对anyRequest
	 * 的配置,则会抛出异常。
	 */
	boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config);

}
/**
 * 授权信息管理器
 * 用于收集系统中所有 AuthorizeConfigProvider 并加载其配置
 *
 */
public interface AuthorizeConfigManager {
	
	/**
	 * @param config
	 */
	void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config);

}
/**
 * 核心模块的授权配置提供器,安全模块涉及的url的授权配置在这里。
 *
 */
@Component
@Order(Integer.MIN_VALUE) //设置调用顺序优先
public class ImoocAuthorizeConfigProvider implements AuthorizeConfigProvider {

	@Autowired
	private SecurityProperties securityProperties;

	@Override
	public boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
		config.antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
				SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_MOBILE,
				SecurityConstants.DEFAULT_SIGN_IN_PROCESSING_URL_OPENID,
				SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*",
				securityProperties.getBrowser().getSignInPage(), 
				securityProperties.getBrowser().getSignUpUrl(),
				securityProperties.getBrowser().getSession().getSessionInvalidUrl()).permitAll();

		if (StringUtils.isNotBlank(securityProperties.getBrowser().getSignOutUrl())) {
			config.antMatchers(securityProperties.getBrowser().getSignOutUrl()).permitAll();
		}
		return false;
	}

}
/**
 * 默认的授权配置管理器,再整个的授权中最后调用
 * 
 */
@Component
public class ImoocAuthorizeConfigManager implements AuthorizeConfigManager {

	//启动后会自动注入所有AuthorizeConfigProvider实现类的Bean
	@Autowired
	private List<AuthorizeConfigProvider> authorizeConfigProviders;

	/* (non-Javadoc)
	 * @see com.imooc.security.core.authorize.AuthorizeConfigManager#config(org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry)
	 */
	@Override
 	public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
		boolean existAnyRequestConfig = false;
		String existAnyRequestConfigName = null;
		
		for (AuthorizeConfigProvider authorizeConfigProvider : authorizeConfigProviders) {
			boolean currentIsAnyRequestConfig = authorizeConfigProvider.config(config);
			if (existAnyRequestConfig && currentIsAnyRequestConfig) {
				throw new RuntimeException("重复的anyRequest配置:" + existAnyRequestConfigName + ","
						+ authorizeConfigProvider.getClass().getSimpleName());
			} else if (currentIsAnyRequestConfig) {
				existAnyRequestConfig = true;
				existAnyRequestConfigName = authorizeConfigProvider.getClass().getSimpleName();
			}
		}
		if(!existAnyRequestConfig){
			config.anyRequest().authenticated();
		}
	}

}
/**
 * 权限模块授权配置
 */
@Component
@Order(Integer.MAX_VALUE) //控制anyRequest()要放在最后
public class RbacAuthorizeConfigProvider implements AuthorizeConfigProvider {

	/* (non-Javadoc)
	 * @see com.imooc.security.core.authorize.AuthorizeConfigProvider#config(org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry)
	 */
	@Override
	public boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
		config
			.antMatchers(HttpMethod.GET, "/fonts/**").permitAll()
			.antMatchers(HttpMethod.GET, 
					"/**/*.html",
					"/admin/me",
					"/resource").authenticated()
			.anyRequest()
				.access("@rbacService.hasPermission(request, authentication)");//具体的鉴权逻辑
		return true;
	}

}
/**
 * 具体的鉴权逻辑
 */
@Component("rbacService")
public class RbacServiceImpl implements RbacService {

	private AntPathMatcher antPathMatcher = new AntPathMatcher();

	@Override
	public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
		Object principal = authentication.getPrincipal();

		boolean hasPermission = false;

		if (principal instanceof Admin) {
			//如果用户名是admin,就永远返回true
			if (StringUtils.equals(((Admin) principal).getUsername(), "admin")) {
				hasPermission = true;
			} else {
				// 读取用户所拥有权限的所有URL
				Set<String> urls = ((Admin) principal).getUrls();
				for (String url : urls) {
					if (antPathMatcher.match(url, request.getRequestURI())) {
						hasPermission = true;
						break;
					}
				}
			}
		}

		return hasPermission;
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值