SpringSecurity | spring security oauth2.0 配置源码分析(一)
概述:在微服务发展迅速的今天,认证授权独立成微服务已是一种趋势,不仅承担着整个系统访问入口的认证和授权,还要易于扩展,能更好的接入第三方服务。而当今Oauth2协议在认证授权领域大行其道,算是功能比较完整的权限协议标准了。spring security oauth2的整合方案应该广为应用,该系列博客就来分析其机制原理。oauth2的配置繁琐复杂,但是只要搞懂每个类的作用,整体来看,并不
·
微信公众号:吉姆餐厅ak
学习更多源码知识,欢迎关注。
概述
在微服务发展迅速的今天,认证授权独立成微服务已是一种趋势,不仅承担着整个系统访问入口的认证和授权,还要易于扩展,能更好的接入第三方服务。而当今Oauth2协议在认证授权领域大行其道,算是功能比较完整的权限协议标准了。spring security oauth2的整合方案应该广为应用,该系列博客就来分析其机制原理。
oauth2的配置繁琐复杂,但是只要搞懂每个类的作用,整体来看,并不复杂。
本着代码先行的原则,第一篇博客就先上配置,并做详细说明。后面两篇对源码逐一分析。
密码模式
1)配置认证模块:
//配置认证服务
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier(value = "authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
private DataSource dataSource;
private OAuth2RequestFactory oAuth2RequestFactory;
@Autowired
private UserDetailsService userDetailsService;
/*配置授权服务器的安全性,这实际上意味着/ oauth / token端点。/ oauth / authorize端点也需要安全,
但这是一个正常的面向用户的端点,
应该与您的UI的其余部分保持一致,所以这里不在这里。默认设置涵盖了最常见的要求,
遵循OAuth2规范的建议,因此您无需在此处执行任何操作即可使基本服务器正常运行。
声明安全约束,哪些允许访问,哪些不允许访问*/
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.allowFormAuthenticationForClients();
//自定义用户验证过滤器,这里认证失败会直接返回。另外在oauth2的TokenEndpoint类中的`/oauth/token`方法,会进行匹配 granter 再次进行用户密码认证
oauthServer.addTokenEndpointAuthenticationFilter(new SecurityTokenEndpointAuthenticationFilter(authenticationManager, oAuth2RequestFactory));
}
/*配置ClientDetailsService,例如声明个别客户端及其属性。请注意,密码授予未启用(即使允许某些客户端),
除非AuthenticationManager提供给AuthorizationServerConfigurer.configure(AuthorizationServerEndpointsConfigurer)。
ClientDetailsService必须声明至少一个客户端或完全形成的自定义,否则服务器将无法启动。
在ClientDetailsServiceConfigurer类里面进行配置,可以有in-memory、jdbc等多种读取方式。
jdbc需要调用JdbcClientDetailsService类,此类需要传入相应的DataSource.*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).clients(clientDetailsService());
}
/* 声明授权和token的端点以及token的服务的一些配置信息,比如采用什么存储方式、token的有效期等
配置授权服务器端点的非安全功能,如令牌存储,令牌自定义,用户批准和授权类型。默认情况下,
您不需要执行任何操作,除非您需要密码授权,否则您需要提供密码AuthenticationManager。*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
endpoints.tokenStore(tokenStore());
endpoints.userDetailsService(userDetailsService);
endpoints.tokenServices(tokenServices());
oAuth2RequestFactory = endpoints.getOAuth2RequestFactory();
}
@Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
@Bean
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
@Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource);
}
@Bean
@Primary
public MyTokenServices tokenServices() {
MyTokenServices tokenServices = new MyTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService());
return tokenServices;
}
}
2)配置授权模块:
//配置授权资源路径
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID);
}
//spring security权限校验核心bean
@Bean
public SecurityAccessDecisionManager accessDecisionManager() {
return new SecurityAccessDecisionManager();
}
//FilterInvocationSecurityMetadataSource的实现类,用来加载权限资源
@Bean
public SecurityMetadataSourceService securityMetadataSource() {
SecurityMetadataSourceService metaSource = new SecurityMetadataSourceService();
return metaSource;
}
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.requestMatchers().antMatchers("/**")
.and()
.authorizeRequests()
.anyRequest().authenticated()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
public <O extends FilterSecurityInterceptor> O postProcess(O fsi) {
fsi.setAccessDecisionManager(accessDecisionManager());
fsi.setSecurityMetadataSource(securityMetadataSource());
return fsi;
}
});
// @formatter:on
}
}
3)权限资源加载类:
/**
* 提供某个资源对应的权限定义,即getAttributes方法返回的结果。
* 此类在初始化时,应该取到所有资源及其对应权限的定义。
* @author zhangshukang
*/
public class SecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {
private Logger log = LoggerFactory.getLogger(getClass());
private static List<UrlDto> resourceMap = Lists.newArrayList();
private static List<UrlDto> defaultMap = Lists.newArrayList();
private static List<UrlDto> holdUpMap = Lists.newArrayList();
@Autowired
private RoleMapper roleMapper;
//饥饿加载,项目启动时将权限资源存储在缓存中
public synchronized void loadMetadataSource() {
try {
// 加载默认策略
List<Map<String, String>> defaultMetaSources = roleMapper.getDefaultMetaSource("0");
defaultMap = parseMetaSource(defaultMetaSources);
defaultMap.stream().forEach(x->{
System.out.println("加载默认策略:url:"+x.getUrl()+"------>permissions:"+x.getPermissions().size()+"------>method:"+x.getMethod());
});
// 加载API策略
List<Map<String, String>> permsMetaSources = roleMapper.getMetaSource();
resourceMap = parseMetaSource(permsMetaSources);
resourceMap.stream().forEach(x->{
System.out.println("加载API策略:url:"+x.getUrl()+"------>permissions:"+x.getPermissions().size()+"------>method:"+x.getMethod());
});
// 加载兜底策略
List<Map<String, String>> holdUpMetaSources = roleMapper.getDefaultMetaSource("1");
holdUpMap = parseMetaSource(holdUpMetaSources);
holdUpMap.stream().forEach(x->{
System.out.println("加载兜底策略:url:"+x.getUrl()+"------>permissions:"+x.getPermissions().size()+"------>method:"+x.getMethod());
});
} catch (Exception e) {
log.error("访问策略加载失败.", e);
}
log.info("loadMetadataSource finish.");
}
// 根据URL,找到相关的权限配置。
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
if (holdUpMap.size() == 0 && defaultMap.size() == 0 && resourceMap.size() == 0) {
loadMetadataSource();
}
FilterInvocation filterInvocation = (FilterInvocation) object;
HttpServletRequest httpRequest = filterInvocation.getHttpRequest();
// 兜底策略
for (UrlDto url : holdUpMap) {
RequestMatcher requestMatcher = new AntPathRequestMatcher(url.getUrl(), url.getMethod());
if (requestMatcher.matches(httpRequest)) {
// TODO 用于检验通过/v1/co/**放行的url,以便在T_API表中配置,将来需要删除
if (httpRequest.getRequestURI().startsWith("/v1/co/"))
log.debug("URL PERM - URL:{} ; Method:{}", httpRequest.getRequestURI(), httpRequest.getMethod());
return convertCollection(url.getPermissions());
}
}
return Lists.newArrayList();
}
}
4)WebSecurity配置:
/**
*
* Created by zhangshukang on 2017/11/9.
*/
@Configuration
@EnableWebSecurity
//@Order(Ordered.HIGHEST_PRECEDENCE)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
auth.authenticationProvider(authenticationProvider());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new ShaPasswordEncoder();
}
@Bean
public SaltSource saltSource() {
return new ShaSaltSource();
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailsService);
authenticationProvider.setPasswordEncoder(passwordEncoder());
authenticationProvider.setSaltSource(saltSource());
return authenticationProvider;
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
//默认/oauth/token路径进行认证
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.requestMatchers().antMatchers(HttpMethod.OPTIONS, "/**")
.and()
.authorizeRequests().antMatchers("/oauth/token").permitAll()
.and()
.csrf().disable()
.headers()
.cacheControl().and()
.xssProtection().disable()
.frameOptions().sameOrigin();
// @formatter:on
}
}
主要的整合配置就这么多了。后面会对配置和执行流程做源码分析。
友链:探果网
更多推荐
已为社区贡献4条内容
所有评论(0)