框架使用SpringBoot 1.5 + Spring Security Oauth2
主要完成了客户端授权
可以通过mysql数据库 将客户端与token信息存储在数据库中。
每次授权会将新的token存储在mysql中,进行客户端验证时,先会从数据库中查询客户端是否存在,存在之后,验证token是否合法。
1.引入依赖
oauth2 依赖于spring security,需要引入spring, mysql,redis, mybatis
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
2. 配置文件
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/oauth2?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
redis:
host: 127.0.0.1
database: 0
mybatis:
mapper-locations: mapper/*.xml
security:
oauth2:
resource:
filter-order: 3
3. 配置
关于oauth2协议相关内容以及授权流程 查看别的博文
主要会使用3个类来配置
- AuthorizationServerConfiguration 授权验证配置
继承AuthorizationServerConfigurerAdapter,配置授权的相关信息,配置的核心都在这里
在这里进行 配置客户端,配置token存储方式等
package oauth.security.client.configauto;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import oauth.security.client.configauto.jdbcdetail.MyJdbcTokenStore;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static final String DEMO_RESOURCE_ID = "*";
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Autowired
private DataSource dataSource;
// 初始化JdbcTokenStore
@Autowired
public TokenStore getTokenStore() {
return new JdbcTokenStore(dataSource);
}
// 自定义数据库存储tokenStore
@Autowired
public TokenStore getMyTokenStore() {
return new MyJdbcTokenStore(dataSource);
}
@Autowired
private TokenStore getRedisTokenStore() {
return new RedisTokenStore(redisConnectionFactory);
}
@Bean // 声明ApplyClientDetailService
public ApplyClientDetailService getClientDetails() {
return new ApplyClientDetailService();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 配置客户端, 用于client认证
clients.withClientDetails(getClientDetails());
/* //使用存在内存中配置
clients.inMemory().withClient("client_1")
.resourceIds(DEMO_RESOURCE_ID)
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("all")
.authorities("client")
.secret("123456");*/
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager); // redis保存token
/* endpoints.tokenStore(getTokenStore()) // 数据库保存token
.authenticationManager(authenticationManager);*/
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
}
}
在配置客户端中,使用了ApplyClientDetailService类,是自定义的获取Client的一个类,继承ClientDetailsService
对Client的访问主要依靠JdbcClientDetailsService类的实现,必须使用官方给出的数据库结构,如果想自定义数据库结构,可以根据需求重写JdbcClientDetailsService类的实现。
package oauth.security.client.configauto;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import oauth.security.client.service.ApplyService;
public class ApplyClientDetailService implements ClientDetailsService {
@Autowired
private ApplyService applyService;
@Autowired
private DataSource dataSource;
@Override
public ClientDetails loadClientByClientId(String applyName) throws ClientRegistrationException {
/*
// 使用mybatic验证client是否存在 ,根据需求写sql
Map clientMap = applyService.findApplyById(applyName);
if(clientMap == null) {
throw new ClientRegistrationException("应用" + applyName + "不存在!");
}*/
// MyJdbcClientDetailsService jdbcClientDetailsService= new MyJdbcClientDetailsService(dataSource, "authentication");
JdbcClientDetailsService jdbcClientDetailsService= new JdbcClientDetailsService(dataSource);
ClientDetails clientDetails = jdbcClientDetailsService.loadClientByClientId(applyName);
return clientDetails;
}
}
- ResourceServerConfiguration 资源配置
配置了资源权限
package oauth.security.client.configauto;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String DEMO_RESOURCE_ID = "*";
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and().requestMatchers().anyRequest()
.and().anonymous()
.and().authorizeRequests()
// .antMatchers("/product/**").access("#oauth2.hasScope('select') and hasRole('ROLE_USER')")
.antMatchers("/**").authenticated(); //配置访问权限控制,必须认证过后才可以访问
}
}
- SecurityConfiguration 安全配置
package oauth.security.client.configauto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
/**
* Created by fcz on 2017/12/28.
*/
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private RedisConnectionFactory redisConnection;
@Bean // 声明ApplyClientDetailService
public ApplyClientDetailService getClientDetails() {
return new ApplyClientDetailService();
}
@Bean
@Override
protected UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("user_1").password("123456").authorities("USER").build());
manager.createUser(User.withUsername("user_2").password("123456").authorities("USER").build());
return manager;
}
@Bean
public TokenStore tokenStore() {
return new RedisTokenStore(redisConnection);
}
@Bean
@Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore());
handler.setRequestFactory(new DefaultOAuth2RequestFactory(getClientDetails()));
handler.setClientDetailsService(getClientDetails());
return handler;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().anyRequest()
.and().authorizeRequests().antMatchers("/oauth/*").permitAll();
}
}
接口访问
使用postMan访问
客户端请求token,POST :http://localhost:8081/oauth/token?grant_type=client_credentials&scope=all&client_id=apply&client_secret=123456
用户请求token,POST :http://localhost:8081/oauth/token?grant_type=password&username=user_1&password=123456&scope=all&client_id=apply&client_secret=123456
Spring2.0 更新
大家使用的时候请注意自己的Spring的版本
Spring1.X 使用SpringSecurityOauth2,Spring2.X使用Spring2.0SecurityOauth2
版本不一致会导致各种各样的错误,SpringSecurity这一次的改变影响比较大。
如果出现问题请先查看是否已经启动Redis和建立数据库。
有其他问题可与我即时沟通。
项目主要以客户端授权验证为主,如需要其他验证酌情添加。
项目地址githup : SpringSecurityOauth2