springsecurity +oauth2.0html
學習本文以前你應該會熟練使用Springboot,並對SpringSecurity
和OAuth2.0
有所理解,若有須要請參考下面的一些內容,簡單理解下相關知識git
Spring Security是一個功能強大、高度可定製的身份驗證和訪問控制框架。它用於保護基於Spring的應用程序。Spring Security是一個專一於向Java應用程序提供身份驗證和受權的框架。與全部Spring項目同樣,Spring安全的真正威力在於它能夠很容易地擴展以知足定製需求。
github
OAuth 2.0是用於受權的行業標準協議。OAuth2.0注重客戶端開發人員的簡單性,同時爲Web應用程序、桌面應用程序、移動電話和客廳設備提供特定的受權流。更多請參考 OAuth2.0web
OAuth2.0模式redis
核心pom依賴以下:spring
<!-- 注意是starter,自動配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- 不是starter,手動配置 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 將token存儲在redis中 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>複製代碼
建立一個rest接口用於後面測試資源api
@Slf4j
@RestController
public class TestSecurityController {
@GetMapping("/product/{id}")
public String getProduct(@PathVariable String id) {
//for debug
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return "product id : " + id;
}
@GetMapping("/order/{id}")
public String getOrder(@PathVariable String id) {
//for debug
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return "order id : " + id;
}
}複製代碼
不少文章寫的特別複雜,其實主要的內容也就分爲下面幾步安全
AuthorizationServerConfigurerAdapter
須要自定義受權服務器,繼承AuthorizationServerConfigurerAdapter
,詳細代碼以下服務器
@Configuration
@EnableAuthorizationServer
public class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static final String DEMO_RESOURCE_ID = "order";
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置兩個客戶端,一個用於password認證一個用於client認證
String secret = new BCryptPasswordEncoder().encode("123456");////對密碼進行加密
clients.inMemory().withClient("client_1")
.resourceIds(DEMO_RESOURCE_ID)
.authorizedGrantTypes("client_credentials", "refresh_token")
.scopes("select")
.authorities("client")
.secret(secret)
.and().withClient("client_2")
.resourceIds(DEMO_RESOURCE_ID)
.authorizedGrantTypes("password", "refresh_token")
.scopes("select")
.authorities("client")
.secret(secret);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(new RedisTokenStore(redisConnectionFactory))
.authenticationManager(authenticationManager);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//容許表單認證
oauthServer.allowFormAuthenticationForClients();
}
}複製代碼
ResourceServerConfigurerAdapter
同上,須要實現本身的資源服務器,繼承ResourceServerConfigurerAdapter
,詳細代碼以下session
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String DEMO_RESOURCE_ID = "order";
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
// Since we want the protected resources to be accessible in the UI as well we need
// session creation to be allowed (it's disabled by default in 2.0.6)
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.requestMatchers().anyRequest()
.and()
.anonymous()
.and()
.authorizeRequests()
// .antMatchers("/product/**").access("#oauth2.hasScope('select') and hasRole('ROLE_USER')")
.antMatchers("/order/**").authenticated();//配置order訪問控制,必須認證事後才能夠訪問
// @formatter:on
}
}複製代碼
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean
@Override
protected UserDetailsService userDetailsService(){
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
String pwd = new BCryptPasswordEncoder().encode("123456");//對密碼進行加密
manager.createUser(User.withUsername("user_1").password(pwd).authorities("USER").build());
manager.createUser(User.withUsername("user_2").password(pwd).authorities("USER").build());
return manager;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().anyRequest()
.and()
.authorizeRequests()
.antMatchers("/oauth/*").permitAll();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}複製代碼
咱們設計的是product
服務能夠匿名訪問,而order
服務須要簽名才能夠訪問,驗證以下:
http://localhost:8080/oauth/token?username=user_1&password=123456&grant_type=password&scope=select&client_id=client_2&client_secret=123456
{
"access_token": "c2340190-48f3-4291-bb17-1e4d51bcb284",
"token_type": "bearer",
"refresh_token": "03ee113c-a942-452a-9918-7ffe24472a7f",
"expires_in": 40399,
"scope": "select"
}複製代碼
http://localhost:8080/oauth/token?grant_type=client_credentials&scope=select&client_id=client_1&client_secret=123456
{
"access_token": "05a4e614-f34b-4c83-9ec1-89ea55c0afd2",
"token_type": "bearer",
"expires_in": 40396,
"scope": "select"
}
複製代碼
http://localhost:8080/product/1
獲得以下數據http://localhost:8080/order/1
,返回數據以下 <oauth>
<error_description>
Full authentication is required to access this resource
</error_description>
<error>unauthorized</error>
</oauth>複製代碼
驗證結果,說明order服務須要簽名才能夠訪問,接下來,咱們輸入簽名訪問order服務。咱們分別利用上面password模式獲取的token,訪問 http://localhost:8080/order/1?access_token=c2340190-48f3-4291-bb17-1e4d51bcb284獲得數據 order id : 1
通用利用client模式獲取的token,訪問 http://localhost:8080/order/1?access_token=05a4e614-f34b-4c83-9ec1-89ea55c0afd2一樣能夠獲得 order id : 1
分支爲springsecurity-oauth2-1
,master爲最終代碼
http://blog.didispace.com/spring-security-oauth2-xjf-1/