Spring Security + OAuth系统环境搭建(一)
最近在做权限管理系统的重构工作,系统基于Spring Security + OAuth架构,整体架构、技术和之前调研的结果差不多,架构调研时有在这篇博客做过简单记录“Spring Cloud微服务下的权限架构调研”,博客未更新,和定稿的架构稍有出入,图画的也有点问题。
前面比较忙,搭建过程没有记录,平时也没有记笔记的习惯,有点惨,写到哪算哪吧~~~
项目是在Spring Cloud基础上改造的,主要记录接入Spring Security + OAuth所做的工作。这里添加security和oauth的相关依赖:
<!-- security -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<!-- oauth2 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
Auth作为认证服务器:
1、新建一个认证服务器配置类,代码如下:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Autowired
private DataSource dataSource; /**
* 注册redisTokenStore,用于配置redis作为token仓库
*
* @return
*/
@Bean
public RedisTokenStore tokenStore() {
return new RedisTokenStore(redisConnectionFactory);
} /**
* 用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
*
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
endpoints.tokenServices(defaultTokenServices())
// 重复使用refresh_token ,指导refresh_token过期,才会产生新的refresh_token
.reuseRefreshTokens(true); } /**
* 配置使用客户端详情服务
*
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService()); } /**
* 用来配置令牌端点
*
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()");
security.checkTokenAccess("isAuthenticated()");
security.allowFormAuthenticationForClients();
} /**
* 注册JdbcClientDetailsService,从数据库获取客户端详情
*/
@Bean
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
} /**
* 配置token服务(仓库、客户端详情服务、是否支持refresh_token以及access_token、refresh_token的有效时间)
*/
@Bean
@Primary
public DefaultTokenServices defaultTokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService());
tokenServices.setAccessTokenValiditySeconds(60 * 60 * 24); // access_token有效期,默认12小时,-1表示永不过期
tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);//refresh_token有效期,默认30天
return tokenServices;
}
}
这里有个东西要注意,在http请求进来之后,首先框架自己会先校验token的正确性。系统oauth采用的是密码模式,熟悉oauth的应该知道,在请求头中必须要带上client_id和client_secret,client_id和client_secret会base64编码后放到请求头的Authorization字段中。这些信息可以在内存、数据库里面维护,这里用的是数据库,就是在clientDetailsService()方法中配置的内容,点开JdbcClientDetailsService的源码可以看到,oauth自己维护了一个oauth_client_details表,所以我们还需要在数据库创建相应的表以及数据。除了oauth_client_details还有维护token相关的表等等,这里没用上。
public JdbcClientDetailsService(DataSource dataSource) {
this.updateClientDetailsSql = DEFAULT_UPDATE_STATEMENT;
this.updateClientSecretSql = "update oauth_client_details set client_secret = ? where client_id = ?";
this.insertClientDetailsSql = "insert into oauth_client_details (client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove, client_id) values (?,?,?,?,?,?,?,?,?,?,?)";
this.selectClientDetailsSql = "select client_id, client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove from oauth_client_details where client_id = ?";
this.passwordEncoder = NoOpPasswordEncoder.getInstance();
Assert.notNull(dataSource, "DataSource required");
this.jdbcTemplate = new JdbcTemplate(dataSource);
this.listFactory = new DefaultJdbcListFactory(new NamedParameterJdbcTemplate(this.jdbcTemplate));
}
JdbcClientDetailsService
CREATE TABLE `oauth_client_details` (
`client_id` varchar(48) NOT NULL,
`resource_ids` varchar(256) DEFAULT NULL,
`client_secret` varchar(256) DEFAULT NULL,
`scope` varchar(256) DEFAULT NULL,
`authorized_grant_types` varchar(256) DEFAULT NULL,
`web_server_redirect_uri` varchar(256) DEFAULT NULL,
`authorities` varchar(256) DEFAULT NULL,
`access_token_validity` int(11) DEFAULT NULL,
`refresh_token_validity` int(11) DEFAULT NULL,
`additional_information` varchar(4096) DEFAULT NULL,
`autoapprove` varchar(256) DEFAULT NULL,
PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
oauth_client_details
2、web配置类
@Configuration
@EnableWebSecurity // 禁用Spring Boot默认的Security配置,配合@Configuration启用自定义配置
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private MyAccessDecisionManager myAccessDecisionManager; /*
* 加密工具
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
} /*
* 认证管理器
*/
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
} /*
* 身份验证配置,用于注入自定义身份验证Bean和密码校验规则
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
} /**
* 无需权限校验直接放行的路径
*/
private final String[] PATH_PASS = {
// 根据实际情况添加
}; /**
* Request层面的配置,对应XML Configuration中的<http>元素
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(PATH_PASS).permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable()
.httpBasic();
// 将自定义的过滤器配置在FilterSecurityInterceptor之前
http.addFilterBefore(myFilterSecurityInterceptor(), FilterSecurityInterceptor.class);
} /**
* Web层面的配置,一般用来配置无需权限校验的路径,也可以在HttpSecurity中配置,但是在web.ignoring()中配置效率更高。
* web.ignoring()是一个忽略的过滤器,而HttpSecurity中定义了一个过滤器链,即使permitAll()放行还是会走所有的过滤器,
* 直到最后一个过滤器FilterSecurityInterceptor认定是可以放行的,才能访问。
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/favor.ioc");
} /**
* 自定义的权限过滤器,后面再写
*/
@Bean
public MyFilterSecurityInterceptor myFilterSecurityInterceptor() {
MyFilterSecurityInterceptor myFilterSecurityInterceptor = new MyFilterSecurityInterceptor();
myFilterSecurityInterceptor.setMyAccessDecisionManager(myAccessDecisionManager);
return myFilterSecurityInterceptor;
}
}
3、上面是一些基本配置,后面还需要自定义身份认证,实现UserDetailsService中的loadUserByUsername(String username)方法,在里面实现自己的身份校验逻辑。
@Service
public class UserDetailsServiceImpl implements UserDetailsService { @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
/*
* 自定义身份校验逻辑,以及根据具体业务做一些其他的事情,比如在redis维护详细的用户信息等等。
* 校验完毕之后将用户权限信息放到grantedAuthorities,最后根据具体校验结果生成User对象并返回。
*/
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
boolean enabled = true; // 可用性 :true:可用 false:不可用
boolean accountNonExpired = true; // 过期性 :true:没过期 false:过期
boolean credentialsNonExpired = true; // 有效性 :true:凭证有效 false:凭证无效
boolean accountNonLocked = true; // 锁定性 :true:未锁定 false:已锁定
User user = new User(username, password,
enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuthorities);
return user;
}
}
4、其实到这里基本的框架都已经差不多了,但是因为我们的Auth还作为资源服务器,还需要做对资源服务器的配置。这一块的配置就简单很多。
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter{ /**
* 无需权限校验直接放行的路径
*/
private final String[] PATH_PASS = {
// 根据实际情况添加
}; @Override
public void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login")
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.anyRequest().authenticated()
.and()
.httpBasic(); }
}
到这里,Auth的环境搭建基本上就算完成了。
在我们的架构中,没有做服务间的权限控制,对外的鉴权工作统一放在网关中处理,所以网关也作为资源服务器。
@Configuration
@EnableResourceServer
@EnableWebSecurity
public class WebSecurityConfig extends ResourceServerConfigurerAdapter {
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Autowired
private MyAccessDecisionManager myAccessDecisionManager; /**
* 无需权限校验直接放行的路径
*/
private final String[] PATH_PASS = {
// 根据实际情况添加
}; /**
* Request层面的配置,对应XML Configuration中的<http>元素
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(AUTH_WHITELIST).permitAll()
.and()
.csrf().disable().exceptionHandling() // 将自定义的过滤器配置在FilterSecurityInterceptor之前
http.addFilterBefore(myFilterSecurityInterceptor(), FilterSecurityInterceptor.class);
} /**
* 资源服务Web层面的配置,这里主要配置了一些鉴权相关的入口,例如异常处理
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
OAuth2WebSecurityExpressionHandler expressionHandler = new OAuth2WebSecurityExpressionHandler();
expressionHandler.setApplicationContext(applicationContext);
return expressionHandler;
} /**
* 注册redisTokenStore,用于配置redis作为token仓库
*
* @return
*/
@Bean
public TokenStore tokenStore() {
return new RedisTokenStore(redisConnectionFactory);
} /**
* 自定义的权限过滤器,后面再写
*/
@Bean
public MyFilterSecurityInterceptor myFilterSecurityInterceptor() {
MyFilterSecurityInterceptor myFilterSecurityInterceptor = new MyFilterSecurityInterceptor();
myFilterSecurityInterceptor.setMyAccessDecisionManager(myAccessDecisionManager);
return myFilterSecurityInterceptor;
} }
资源服务器的配置相对还是比较少的,因为需要在网关做鉴权,所以这里也配置了自定义过滤器。另外,还继承ZuulFilter类实现了过滤器,关于过滤器这块后面再统一写。
到这呢,大体的环境搭建基本已经OK了,基本的功能能够使用了,后面的东西会稍微细一点,慢慢整理。
Spring Security + OAuth系统环境搭建(一)的更多相关文章
- Spring Security OAuth 2.0
续·前一篇<OAuth 2.0> OAuth 2.0 Provider 实现 在OAuth 2.0中,provider角色事实上是把授权服务和资源服务分开,有时候它们也可能在同一个应用中, ...
- Spring Security OAuth笔记
因为工作需要,系统权限安全方面可能要用到Spring Security OAuth2.0,所以,近几天了解了一下OAuth相关的东西.目前好像还没有系统的学习资料,学习主要是通过博客,内容都是大同小异 ...
- Spring Security OAuth 2开发者指南译
Spring Security OAuth 2开发者指南译 介绍 这是用户指南的支持OAuth 2.0.对于OAuth 1.0,一切都是不同的,所以看到它的用户指南. 本用户指南分为两部分,第一部分为 ...
- 【微服务】 数据库案例理解Spring Security OAuth
突然被问,你是做技术的怎么不走技术路线呢?是啊~仔细想想至今做了这么多年的技术,研发过的系统&产品五花八门,涉及到的领域各行各业:政府.军队.公安.国安.石油&石化.金融.教育.华为等 ...
- Spring Security OAuth 格式化 token 输出
个性化token 背景 上一篇文章<Spring Security OAuth 个性化token(一)>有提到,oauth2.0 接口默认返回的报文格式如下: { "ac ...
- Spring Security OAuth 笔记
1 单点登录 关于单点登录的原理,我觉得下面这位老哥讲的比较清楚,有兴趣可以看一下,下面我把其中的重点在此做个笔记总结 https://juejin.cn/post/6844904079274197 ...
- Spring Security OAuth正式终止维护,已从官网下架
Spring Security团队正式宣布Spring Security OAuth终止维护. 目前官网的主页已经高亮提醒彻底停止维护. 旧的Spring Security OAuth项目终止到2.5 ...
- 学习Spring Security OAuth认证(一)-授权码模式
一.环境 spring boot+spring security+idea+maven+mybatis 主要是spring security 二.依赖 <dependency> <g ...
- Using JWT with Spring Security OAuth
http://www.baeldung.com/spring-security-oauth-jwt ************************************************** ...
随机推荐
- JAVA解决前端跨域问题。
什么是跨域? 通俗来说,跨域按照我自己的想法来理解,是不同的域名之间的访问,就是跨域.不同浏览器,在对js文件进行解析是不同的,浏览器会默认阻止,所以 现在我来说下用java代码解决前端跨域问题. 用 ...
- java中的值传递和引用传递有什么区别呀?
值传递: (形式参数类型是基本数据类型和String):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参 ...
- 第五周助教工作总结——NWNU李泓毅
第五周助教总结 注:因第四次实验安排两个标准时间完成,因此本周未提交完整作业. 本周心得: 第四次实验进行过半,八组同学都在实验课上进行了一次中期总结,并形成书面总结在微信群中讨论. 根据各组同学的中 ...
- ABP的确认框
使用之前,是需要添加对abp.sweet-alert.js的引用,否则就无法正常使用. 确认框 abp.message.info('some info message', 'some optional ...
- Selenium+PyCharm环境搭建
一.首先安装python并配置好环境变量 二.安装selenium 安装文件夹在安装的python文件夹下,例:D:\Program\python\Lib\site-packages\selenium ...
- HTTP协议 与 TCP协议 的区别
TCP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据. TCP/IP和HTTP协议的关系,从本质上来说,二者没有可比性,我们在传输数据时,可以只使用(传输 ...
- idea配置.gitignore后无法起作用
1)要先进入项目包所在的文件夹 2)git rm -r --cached . ://后面有个点3)git add . ;后面有个点4)git commit -m "update .giti ...
- python 单例模式的四种实现方法及注意事项
一.模块单例 Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码. #foo1.py clas ...
- nmap 介绍
原文地址:http://drops.wooyun.org/tips/2002 原文地址:http://infotechbits.wordpress.com/2014/05/04/introductio ...
- 继承Thread类和实现Runnable接口
一.采用继承Thread类方法的特点: 优势:编写简单,如果需要访问当前的线程,只需要使用this,并可以在run()方法中调用其他线程的方法: 劣势:线程已经继承了Thread类,不能继承其他的父类 ...