最近在学习spring boot 权限相关模块,网上也有很多关于这方面的教程说明,发现需要搜索很多资料才能把spring security + oauth2 demoi搭建起来。希望这篇文章能帮助许多正在学习的同学们解决问题。
如果有同学只想整合 spring security 请到我的github 下载代码学习:https://github.com/ltztao/test-security
Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.5.RELEASE</version>
</dependency>
注意:spring-security-oauth2>=2.3.0,<2.3.4中检测到已知的高严重性安全漏洞,请合理引用依赖
认证服务安全配置
/**
* 认证服务器配置
* @author ltzhang
* @time 2019-11-28
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
/**
* 注入权限验证控制器 来支持 password grant type
*/
@Autowired
private AuthenticationManager authenticationManager;
/**
* 注入userDetailsService,开启refresh_token需要用到
*/
@Autowired
private MyUserDetailsService userDetailsService;
/**
* 数据源
*/
@Autowired
private DataSource dataSource;
@Autowired
private RedisConnectionFactory connectionFactory;
@Autowired
private MyOAuth2WebResponseExceptionTranslator webResponseExceptionTranslator;
@Bean
public TokenStore tokenStore() {
return new RedisTokenStore( connectionFactory );
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//开启密码授权类型
endpoints.authenticationManager(authenticationManager);
//配置token存储方式
endpoints.tokenStore(tokenStore());
//自定义登录或者鉴权失败时的返回信息
endpoints.exceptionTranslator(webResponseExceptionTranslator);
//要使用refresh_token的话,需要额外配置userDetailsService
endpoints.userDetailsService( userDetailsService );
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
}
此处token信息都存放在redis中,client信息存放在数据库中(如果要配置在内存中,请自行百度)
大家如果想讲token信息保存在数据请自行做相关修改,oauth2 相关数据表 https://github.com/ltztao/spring-security-oauth2/blob/master/init-oauth2.sql
-- 由于当前token存放在redis中没使用jdbc且只有令牌存放在数据库中达到可管理,故只要创建oauth_client_details即可
-- 如若token存放数据库中则需要创建以下所有表
CREATE TABLE `oauth_client_details` (
`client_id` varchar(128) 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;
数据库字段说明 http://andaily.com/spring-oauth-server/db_table_description.html
资源服务配置
/**
* 资源提供端的配置
* @author ltzhang
* @time 2019-11-28
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private RestAuthAccessDeniedHandler deniedHandler;
@Autowired
private MyAuthenticationEntryPoint authenticationEntryPoint;
/**
* 这里设置需要token验证的url
* 可以在WebSecurityConfigurerAdapter中排除掉,
* 对于相同的url,如果二者都配置了验证
* 则优先进入ResourceServerConfigurerAdapter,进行token验证。而不会进行
* WebSecurityConfigurerAdapter 的 basic auth或表单认证。
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/hi")
.and()
.authorizeRequests()
.antMatchers("/hi")
.authenticated();
//需要的时候创建session,支持从session中获取认证信息,ResourceServerConfiguration中
//session创建策略是stateless不使用,这里其覆盖配置可创建session
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.authenticationEntryPoint(authenticationEntryPoint)
.accessDeniedHandler(deniedHandler);
//此处是关键,默认stateless=true,只支持access_token形式,
// OAuth2客户端连接需要使用session,所以需要设置成false以支持session授权
resources.stateless(false);
}
}
Web安全配置
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class BrowerSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationSuccessHandler successHandler;
@Autowired
private MyAuthenticationFailHandler failHandler;
@Autowired
private AjaxSessionInformationExpiredStrategy expiredStrategy;
@Autowired
private SecurityAuthenticationProvider provider;
@Autowired
private RestAuthAccessDeniedHandler deniedHandler;
@Autowired
private MyLogoutSuccessHandler logoutSuccessHandler;
@Autowired
private MyAuthenticationEntryPoint entryPoint;
/**
* 如若需从数据库动态判断权限则实现 AccessDecisionManager
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//"/login"不进行权限验证
.antMatchers("/login").permitAll()
// .antMatchers("/favicon.ico").permitAll()
.and()
.authorizeRequests()
.anyRequest().authenticated() //其他的需要登陆后才能访问
.and()
.formLogin()
//loginProcessingUrl用于指定前后端分离的时候调用后台登录接口的名称
.loginProcessingUrl("/login")
.usernameParameter("username")
.passwordParameter("password")
.successHandler(successHandler)
.failureHandler(failHandler).permitAll()
.and()
//loginProcessingUrl用于指定前后端分离的时候调用后台注销接口的名称
.logout().logoutUrl("/logout")
.invalidateHttpSession(true)
.logoutSuccessHandler(logoutSuccessHandler)
.and()
//配置没有权限的自定义处理类
.exceptionHandling()
.accessDeniedHandler(deniedHandler)
.authenticationEntryPoint(entryPoint); // 自定义处理未登录
// 设置跨域问题
http.cors().and().csrf().disable();
http.sessionManagement()
.maximumSessions(1) // 同一个账号只能在一个地方登陆
.expiredSessionStrategy(expiredStrategy);
}
/**
* 自定义验证逻辑
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth){
auth.authenticationProvider(provider);
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception{
return super.authenticationManager();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
当前demo中如:token过期,登录失败,登录成功...都处理返回json,如果其它需求请自行处理。
测试
完整的代码下载链接:https://github.com/ltztao/spring-security-oauth2
参考资料均来自网上