前言
- 之前我们看了Spring Security在控制授权这一块他的核心代码,我们看到最后请求通过或者不通过都是转成了权限的表达式,然后交给了一个WebExpressionVoter去评估你的表达式。这个表达式评估的结果是true,那么访问就通过,如果评估的结果是false,那么访问就不通过。那么问题来了,SpringSecurity有多少种权限表达式?每一种权限表达式的写法是怎样的?代表的意思是什么?
内容
1. 表达式说明
上面的表达式都是在WebSecurityConfig可以配置的: 每一个表达式都是对应了HttpSecurity的一个方法,并且是跟在antMatchers之后的。
比如:antMatchers("xxx").permitAll();
antMatchers---指定url
表达式---指定授权
如果需要多个表达式合并统一起来,需要自己通过.access来自己定义:
另一个需求是我们能不能通过.access自定义逻辑:让系统读取我们自己的逻辑而不是使用系统的默认逻辑。
2. 剥离用户自己模块的url服务
2.1 抽离思想
目前我们的安全配置是写在我们的安全模块代码spring-security-core里面的,不管是spring-security-web和spring-security-app.都有一些针对url的安全配置。但是问题是这些url有些是我们的安全模块提供的 ,比如说一下url:
都是安全模块提供的。
但是用户注册("/user/register"),和获取用户的url("/user/*"),他其实是我们的spring-security-demo项目提供的。也就是使用我们安全模块的人提供的安全服务。对于安全模块来说,事先我们并不知道谁会使用我们的安全模块。我也不知道使用此安全模块的url服务。所以针对于使用安全模块的url服务我们应该剥离出去。
实现思路很简单,我们提供一个接口,在我们自己的权限模块里面去实现这个接口(把和安全模块相关的配置写到这个接口里面)。如果是用户模块的话,用户模块自己去实现此接口(将用户模块的url写到用户模块中) 如果我们应用A都需要多有的url服务,那么我们就让应用A去依赖这两个模块。应用A也有这个AuthorizeConfigProvider的实现。所以在应用A的spring容器中 一共有3个模块的权限配置提供者的实现:他们是权限模块、用户模块、应用A实现模块。
1.权限模块:权限模块自身的url权限配置
2.用户模块:用户模块的url权限配置
3.应用A实现模块:应用A特有的url权限配置
最后,在我们的权限模块core中只需要提供一个:AuthorizeConfigManager类,这个类的作用是:把Spring容器里面所有AuthorizeConfigProvider接口的实现全部收集起来。然后按照各个实现的配置给配置好。
假如我们现在有一个应用B,应用B也有自己的AuthorizeConfigProvider的实现并且依赖了权限模块实现、用户模块实现,此时AuthorizeConfigManager类也会收集起三个模块:权限模块、用户模块、应用A实现模块。最终应用A和应用B权限是不同的,但是我们的权限模块core是不管的。
2.2 抽离-代码实现
2.2.1 spring-security-core创建AuthorizeConfigProvider
- 我们创建包:com.yxm.security.core.authorize,然后在此包下创建接口:AuthorizeConfigProvider
- 我们从spring-security-web的WebSecurityConfig的配置里面获取授权url开始的权限对象:
- authorizeRequests()返回的对象是:
-
我们将其封装到授权配置provider中去:
public interface AuthorizeConfigProvider { void configure(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config); }
2.2.2 core实现AuthorizeConfigProvider
MyAuthorizeConfigProvider实现AuthorizeConfigProvider 接口,并申明为Spring的组件。
@Component
public class MyAuthorizeConfigProvider implements AuthorizeConfigProvider {
@Autowired
private SecurityProperties securityProperties;
@Override
public void configure(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
config.antMatchers(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_MOBILE,
securityProperties.getBrowser().getLoginPage(),
SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX+"/*",
securityProperties.getBrowser().getSignUpUrl())
.permitAll();
}
}
2.2.3 core中定义AuthorizeConfigManager
public interface AuthorizeConfigManager {
void configure(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config);
}
2.2.4 core中定义AuthorizeConfigManager的实现:MyAuthorizeConfigManager
@Component
public class MyAuthorizeConfigManager implements AuthorizeConfigManager {
/**
* 作用:把系统的provider全部收集起来
* @param config
*/
@Autowired
private Set<AuthorizeConfigProvider> authorizeConfigProviders;
@Override
public void configure(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
for (AuthorizeConfigProvider authorizeConfigProvider:authorizeConfigProviders){
authorizeConfigProvider.configure(config);
}
//除了上面配置的所有权限外,其他请求都需要授权
config.anyRequest().authenticated();
}
}
2.2.5 web里面改造
@Configuration
public class WebSecurityConfig extends AbstractChannelSecurityConfig {
@Autowired
private ValidateCodeSecurityConfig validateCodeSecurityConfig;//验证码过滤器配置
@Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; //短信验证码授权配置
@Autowired
private SpringSocialConfigurer mySocialSecurityConfig;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private SecurityProperties securityProperties;
@Autowired
private DataSource dataSource;
@Autowired
private AuthorizeConfigManager authorizeConfigManager;
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
//
//因为是Jdbc操作,所以我们需要注入数据源:org.springframework.jdbc.core.support.JdbcDaoSupport
//tokenRepository继承org.springframework.jdbc.core.support.JdbcDaoSupport
tokenRepository.setDataSource(dataSource);
System.out.println("PersistentTokenRepository--dataSource:>dataSource");
//tokenRepository.setCreateTableOnStartup(true);//系统启动的时候创建:CREATE_TABLE_SQL表
return tokenRepository;
}
/**
* 定义web安全配置类:覆盖config方法
* 1.参数为HttpSecurity
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
applyPasswordAuthenticationConfig(http);
http.apply(validateCodeSecurityConfig)
.and()
.apply(smsCodeAuthenticationSecurityConfig)
.and()
.apply(mySocialSecurityConfig)//配置第三方social
.and()
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())//配置token失效秒数
.userDetailsService(userDetailsService)
.and()
.csrf().disable();
authorizeConfigManager.configure(http.authorizeRequests());
}
}
2.2.6 spring-security-app里面改造
@Configuration
@EnableResourceServer
public class MyResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
@Autowired
private SmsCodeAuthenticationSecurityConfig smsCodeAuthenticationSecurityConfig; //短信验证码授权配置
@Autowired
private SpringSocialConfigurer mySocialSecurityConfig;
@Autowired
private SecurityProperties securityProperties;
@Autowired
private ValidateCodeSecurityConfig validateCodeSecurityConfig;//验证码过滤器配置
@Autowired
private OpenIdAuthenticationSecurityConfig openIdAuthenticationSecurityConfig;
@Autowired
private AuthorizeConfigManager authorizeConfigManager;
@Override
public void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage(SecurityConstants.DEFAULT_UNAUTHENTICATION_URL)
.loginProcessingUrl(SecurityConstants.DEFAULT_LOGIN_PROCESSING_URL_FORM)
.successHandler(myAuthenticationSuccessHandler)
.failureHandler(myAuthenticationFailureHandler);
http.apply(validateCodeSecurityConfig)
.and()
.apply(smsCodeAuthenticationSecurityConfig)
.and()
.apply(mySocialSecurityConfig)//配置第三方social
.and()
.apply(openIdAuthenticationSecurityConfig)
.and()
.csrf().disable();
authorizeConfigManager.configure(http.authorizeRequests());
}
}
2.2.7 spring-security-demo改成
除了core里面的配置,其他用户注册和用户获取的授权都是demo项目自己的配置。
首先先实现:AuthorizeConfigProvider的类:DemoAuthorizeConfigProvider
@Component
public class DemoAuthorizeConfigProvider implements AuthorizeConfigProvider {
@Override
public void configure(ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry config) {
config.antMatchers("/user").hasRole("ADMIN");
}
}
然后在授权时候返回的用户配置上即可:
3.测试
我们访问登录页面http://127.0.0.1:8088/login.html
访问:http://127.0.0.1:8088/user时候:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。