spring-security-oauth2 (二十七) Spring Security 权限表达式

权限表达式

通过源码分析可知,最终的配置都会转成ExpressionUrlAuthorizationConfigurer.AuthorizedUrl并进行投票决定。

常见表达式如下:

如何写联合表达式呢?就是既满足A条件,也满足B条件

.antMatchers("xx").access("hasRole('ROLE_USER') and hasRole('ROLE_SUPER')")

 这个都是spring自己的权限表达式,那么我们是否可以自定义实现自己的权限规则呢?可以的!这个我们下一章再看怎么实现。

 

权限业务配置分离

如何将权限模块配置和业务模块配置想分离呢?因为权限模块并不清楚业务模块的权限拦截规则。

实现思路如下:

  1. 提供AuthorizeConfigProvider接口,提供权限设置
  2. 权限模块的通用配置实现该接口,然后进行配置,业务模块按需也实现该接口进行配置
  3. 最后使用 AuthorizeConfigManager类来管理所有的AuthorizeConfigProvider实现
  4. 拿到所有的配置后,进行统一设置

代码实现如下:

权限控制接口及实现 

package com.rui.tiger.auth.core.authorize;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;

/**
 * 自定义权限控制接口
 * @author CaiRui
 * @date 2019-05-06 12:02
 */
public interface AuthorizeConfigProvider {

	/**
	 * @param config
	 * @return 返回的boolean表示配置中是否有针对anyRequest的配置。在整个授权配置中,
	 * 应该有且仅有一个针对anyRequest的配置,如果所有的实现都没有针对anyRequest的配置,
	 * 系统会自动增加一个anyRequest().authenticated()的配置。如果有多个针对anyRequest
	 * 的配置,则会抛出异常。
	 */
	boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config);
}

通用权限设置

package com.rui.tiger.auth.core.authorize;

import com.rui.tiger.auth.core.properties.SecurityConstants;
import com.rui.tiger.auth.core.properties.SecurityProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.stereotype.Component;

/**
 * 通用权限接口配置管理
 * @author CaiRui
 * @date 2019-05-06 12:08
 */
@Component
@Slf4j
@Order(Integer.MIN_VALUE)
public class CommonAuthorizeConfigProvider implements AuthorizeConfigProvider  {

	@Autowired
	private SecurityProperties securityProperties;

	@Override
	public boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {

		config.antMatchers(
				SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,//权限认证
				SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,//手机
				SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_OPENID,//openId
				securityProperties.getBrowser().getLoginPage(),//登录页面
				SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*",// /captcha/* 验证码放行
				securityProperties.getBrowser().getSignupUrl(),
				securityProperties.getBrowser().getLoginOut(),
				securityProperties.getBrowser().getSession().getInvalidSessionUrl()
		).permitAll();//放行

		return false;
	}
}

业务模块权限设置

package com.rui.tiger.auth.demo.security;

import com.rui.tiger.auth.core.authorize.AuthorizeConfigProvider;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.stereotype.Component;

/**
 * 业务模块权限配置实现
 * @author CaiRui
 * @date 2019-05-06 12:16
 */
@Component
@Order(Integer.MAX_VALUE)
public class DemoAuthorizeConfigProvider implements AuthorizeConfigProvider {

	@Override
	public boolean config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
		config.antMatchers(
				"/user/regist", // 注册请求
				"/error",
				"/connect/*",
				"/auth/*",
				"/signin",
				"/social/signUp",  // app注册跳转服务
				"/swagger-ui.html",
				"/swagger-ui.html/**",
				"/webjars/**",
				"/swagger-resources/**",
				"/v2/**"
		).permitAll();

		/*
		 利用@Order注解实现CommonAuthorizeConfigProvider中的放行路径不需要自定义权限验证
		 */
		config.anyRequest()
				.access("@rbacService.hasPermission(request,authentication)");

		return true;
	}
}

权限接口管理器及实现

package com.rui.tiger.auth.core.authorize;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;

/**
 * 权限控制接口持有管理者
 * @author CaiRui
 * @date 2019-05-06 12:05
 */
public interface AuthorizeConfigManager {

	void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config);
}
package com.rui.tiger.auth.core.authorize;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;

/**
 * @author CaiRui
 * @date 2019-05-06 12:12
 */
@Component
public class DefaultAuthorizeConfigManager implements AuthorizeConfigManager  {

	//有依赖顺序 通过@Order控制  不用set
	@Autowired
	private List<AuthorizeConfigProvider> providers;

	@Override
	public void config(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {

		boolean existAnyRequestConfig = false;
		String existAnyRequestConfigName = null;

		for (AuthorizeConfigProvider authorizeConfigProvider : providers) {
			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();
		}
	}
}

浏览器模块配置进行调整(app模块同理),就是将原来的配置抽离出去。

@Override
	protected void configure(HttpSecurity http) throws Exception {
		/**
		 * 表单密码配置
		 */
		applyPasswordAuthenticationConfig(http);
		http
				.apply(captchaSecurityConfig)
					.and()
				.apply(smsAuthenticationSecurityConfig)
					.and()
				.apply(tigerSpringSocialConfigurer)
					.and()
				.rememberMe()
				.tokenRepository(persistentTokenRepository())
				.tokenValiditySeconds(securityProperties.getBrowser().getRemberMeSeconds())
				.userDetailsService(userDetailsService)
					.and()
				.sessionManagement()
				.invalidSessionStrategy(invalidSessionStrategy)//session失效策略
				.maximumSessions(securityProperties.getBrowser().getSession().getMaximumSessions())//最大session并发数
				.maxSessionsPreventsLogin(securityProperties.getBrowser().getSession().isMaxSessionsPreventsLogin())//true达到并发数后阻止登录,false 踢掉之前的登录
				.expiredSessionStrategy(sessionInformationExpiredStrategy)//并发策略
				.and()
				.and()
				.logout()
				.logoutUrl("/loginOut") //默认logout
				//.logoutSuccessUrl("") url和Handler只能配置一个
				.logoutSuccessHandler(tigerLogoutSuccessHandler)
				.deleteCookies("JSESSIONID")//清楚cook键值
					.and()
				.csrf().disable();
		//配置管理
		authorizeConfigManager.config(http.authorizeRequests());

	}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值