SpringBoot+Spring Security Oauth2實現客戶端授權
阿新 • • 發佈:2019-01-09
框架使用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();
}
}
介面訪問
Spring2.0 更新
大家使用的時候請注意自己的Spring的版本
Spring1.X 使用SpringSecurityOauth2,Spring2.X使用Spring2.0SecurityOauth2
版本不一致會導致各種各樣的錯誤,SpringSecurity這一次的改變影響比較大。
如果出現問題請先檢視是否已經啟動Redis和建立資料庫。
有其他問題可與我即時溝通。
專案主要以客戶端授權驗證為主,如需要其他驗證酌情新增。