OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。
实现思路:OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。"客户端"不能直接登录"服务提供商",只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。
"客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。
所以与spring security 相比 oauth有更广泛的应用kong空间。
spring boot 集成oauth2.0
1.加入依赖
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
注意:这是在上一篇博客项目基础上进行的,所以需要先移步Spring boot 入门教程-集成security
创建Oauth2.0需要创建三个相关的表,直接使用官方的SQL脚本即可生成(不要修改表名和字段名).
OAuth2 官方的项目中可以找到:https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql
-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
`token_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`token` blob NULL,
`authentication_id` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`user_name` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`client_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`authentication` blob NULL,
`refresh_token` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`authentication_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
`client_id` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`authorities` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`access_token_validity` int(11) NULL DEFAULT NULL,
`refresh_token_validity` int(11) NULL DEFAULT NULL,
`additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
`token_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`token` blob NULL,
`authentication` blob NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
2.在上一篇博客项目基础上 还有两项需要配置
a.配置资源服务器 b.配置认证服务器
3.配置认证服务器
@Configuration
@EnableAuthorizationServer // 这个注解告诉 Spring 这个应用是 OAuth2 的授权服务器//
// 提供/oauth/authorize,/oauth/token,/oauth/check_token,/oauth/confirm_access,/oauth/error
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
@Qualifier("dataSource")
private DataSource dataSource;
@Autowired
private UserDetailsService userDetailsService;
@Bean
public TokenStore tokenStore() {
// return new InMemoryTokenStore(); //使用内存中的 token store
return new JdbcTokenStore(dataSource); ///使用Jdbctoken store
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource)
.withClient("client")
.secret(new BCryptPasswordEncoder().encode("123456"))
.authorizedGrantTypes("password", "refresh_token")//允许授权范围
.authorities("ROLE_ADMIN","ROLE_USER")//客户端可以使用的权限
.scopes( "read", "write")
.accessTokenValiditySeconds(7200)
.refreshTokenValiditySeconds(7200);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);//必须设置 UserDetailsService 否则刷新token 时会报错
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();//允许表单登录
}
}
4.配置资源服务器
@Configuration
@EnableResourceServer //这个类表明了此应用是OAuth2 的资源服务器,此处主要指定了受资源服务器保护的资源链接
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//禁用了 csrf 功能
.authorizeRequests()//限定签名成功的请求
.antMatchers("/decision/**","/govern/**").hasAnyRole("USER","ADMIN")
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/test/**").authenticated()//必须认证过后才可以访问
.anyRequest().permitAll()//其他没有限定的请求,允许随意访问
.and().anonymous();//对于没有配置权限的其他请求允许匿名访问
}
}
5.对WebSecurityConfig 进行修改,因为加入了oauth 2.0 的配置,所以该文件之前的权限设置可以删掉,访问控制交给资源服务器只保留“/oauth/**,"/login/**”,"/logout/**",修改后的WebSecurityConfig
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean() ;
}
/**
* 配置用户签名服务 主要是user-details 机制,
*
* @param auth 签名管理器构造器,用于构建用户具体权限控制
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
/**
* 用来配置拦截保护的请求
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//不拦截 oauth 开放的资源
http.csrf().disable();
http.requestMatchers()//使HttpSecurity接收以"/login/","/oauth/"开头请求。
.antMatchers("/oauth/**", "/login/**", "/logout/**")
.and()
.authorizeRequests()
.antMatchers("/oauth/**").authenticated()
.and()
.formLogin();
}
}
到这整合就完成了。
获取token :
http://localhost:18088/oauth/token?username=admin&password=admin&grant_type=password&client_id=client&client_secret=123456&grant_type=refresh_token
返回:
{
"access_token": "624d8e84-e981-484b-a064-1d8f5997e4fb",
"token_type": "bearer",
"refresh_token": "ca0d41c8-d808-4211-8cab-5da5bfe6c6db",
"expires_in": 5696,
"scope": "read write"
}
刷新令牌:
http://localhost:18088/oauth/token?grant_type=refresh_token&client_id=client&client_secret=123456&refresh_token=ca0d41c8-d808-4211-8cab-5da5bfe6c6db
返回数据:
{
"access_token": "75b23bfc-c0d5-425f-b780-df8fff60d256",
"token_type": "bearer",
"refresh_token": "ca0d41c8-d808-4211-8cab-5da5bfe6c6db",
"expires_in": 7199,
"scope": "all read write"
}
大功告成!
补充:客户端模式:
授权服务器增加配置如下:
.and().withClient("client_1")
.secret(passwordEncoder().encode("123456"))
.authorizedGrantTypes("client_credentials")
.scopes("read", "write")
.authorities("client_credentials")
.accessTokenValiditySeconds(7200)
资源服务器做相应的修改,授权成功后即可访问:
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//禁用了 csrf 功能
.authorizeRequests()//限定签名成功的请求
.antMatchers("/test/**","/admin/**").authenticated()//签名成功后可访问
// .antMatchers("/admin/login","/oauth/**").permitAll()
.anyRequest().permitAll()//其他没有限定的请求,允许访问
.and().anonymous()//对于没有配置权限的其他请求允许匿名访问
.and().formLogin()//使用 spring security 默认登录页面
.and().httpBasic();//启用http 基础验证
}
请求授权:
http://localhost:18088/oauth/token?grant_type=client_credentials&client_id=client_1&client_secret=123456
返回:
{
"access_token": "47c31bff-fd5c-42de-879f-c3ddcec5c7c7",
"token_type": "bearer",
"expires_in": 4674,
"scope": "read write"
}
请求其他资源:
http://localhost:18088/test/detail?access_token=47c31bff-fd5c-42de-879f-c3ddcec5c7c7