上片文章已经整合Spring Security Springboot整合Spring Security Oauth2.0 + JWT (一),由于现在开发的项目基本上都是前后端分离,所以选择采用的Spring Security Oauth2.0。
整合Oauth2.0
在第一篇的基础之上实现
添加依赖
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
编写认证服务器AuthorizationServerConfiguration继承AuthorizationServerConfigurerAdapter
(一)基于内存存储用户信息和令牌
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Bean
public TokenStore tokenStore(){
//基于内存存储令牌
return new InMemoryTokenStore();
}
/**
* 配置客户端信息
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//基于内存存储
clients.inMemory()
.withClient("clientId")//客户端标识
.secret(passwordEncoder.encode("secret"))//客户端安全码
.authorizedGrantTypes("authorization_code","password","refresh_token")//客户端授权类型
.scopes("app")//客户端授权范围
.redirectUris("http://www.baidu.com");//注册回调地址
}
}
通过Get请求访问认证服务器获取授权码,这儿根据上面配置的客户端信息
http://localhost:8088/oauth/authorize?client_id=clientId&response_type=code
根据登录表单提交验证后
会跳转到之前配置的回调地址,并在后面返回一个授权码
拿到授权码后通过Post请求访问认证服务器获取令牌access_token
被拦截了-_-||,通过配置httpSecurity里面放行/oauth/**,不过没什么卵用,但是采用下面url请求就可以,原理还没搞清楚,需要研究下
http://clientId:secret@localhost:8088/oauth/token
携带参数 code:gvUye7 grant_type:authorization_code
看过其他博客,可以在认证服务器里面添加令牌端点约束解决这个问题的
/**
* 配置令牌端点的安全约束
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")//tokenKey这个endpoint完全公开
.checkTokenAccess("permitAll()")//checkToken这个endpoint完全公开
.allowFormAuthenticationForClients();//允许表单验证
}
这样子过后可以添加参数拿到token了,并能使用check_token来查看token信息
上面的方式是采用授权码模式获取的token,采用密码模式测试一下
提示Unsupported grant type,字面意思是不支持的授权类型,原因是密码模式需要在认证服务器中配置AuthenticationManager,参考OAuth2密码模式提示Unsupported grant type: password
在WebSecurityConfiguration类中添加
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
在认证管理器的令牌端点配置中添加
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
运行发现正确拿到token
默认的端点URL
/oauth/authorize:授权端点
/oauth/token:令牌端点
/oauth/confirm_access:用户确认授权提交端点
/oauth/error:授权服务错误信息端点
/oauth/check_token:用于资源服务访问的令牌解析端点
(二)通过JDBC存储客户端信息和令牌
建立Oauth数据库表,直接用官方的SQL表结构 SQL表结构,这里面有sql语句,直接copy执行就好了。关于OAuth2相关数据表字段的详细说明可以参考OAuth2 数据库表说明文档。
有张名为oauth_clients_details的表用于存储客户端信息的,在这里先配置它,跟上面代码内存配置的差不多
配置认证服务器
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Resource
private DataSource dataSource;
@Autowired
private AuthenticationManager authenticationManager;
@Bean
public TokenStore tokenStore() {
//基于jdbc存储token
return new JdbcTokenStore(dataSource);
}
/**
* 令牌端点,设置令牌
*
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager);
}
/**
* 基于jdbc存储客户端信息,需要先进行配置
*
* @return
*/
public ClientDetailsService clientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}
/**
* 配置客户端信息
*
* @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()")//tokenKey这个endpoint完全公开
.checkTokenAccess("permitAll()")//checkToken这个endpoint完全公开
.allowFormAuthenticationForClients();//允许表单验证
}
}
根据数据库的配置,同样访问/oauth/authorize接口,得到下面的结果
拿到code后跟之前一样操作获取token,不过client_id和client_secret要和自己配置的一样
编写资源服务器ResourceServerConfiguration 继承ResourceServerConfigurerAdapter
这个案例为单体架构的项目,认证服务器和资源服务器放到一起的。做分布式架构的项目时候可以将它们独立分开来,做一个认证服务器,每个微服务都是一个资源服务器
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
//资源id,这个资源id,数据库客户端表配置的需要有这个
public static final String RESOURCE_ID = "res";
@Autowired
private TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore)
.resourceId(RESOURCE_ID)
.stateless(false);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated().and()
.requestMatchers().antMatchers("/api/**");
}
}
用前面的方式拿到token以后,每一次请求携带token
访问有权限的接口正常的
由于该接口资源上添加了某权限标识,当前登录用户没有该权限
此时的项目结构
整合的Jwt在下篇文章
Springboot整合Spring Security Oauth2.0 + JWT (三)