3、SpringCloud鑑權之OAuth2.0(上篇)

鑑權中心

springsecurity +oauth2.0html

一、前言

必備知識

學習本文以前你應該會熟練使用Springboot,並對SpringSecurityOAuth2.0有所理解,若有須要請參考下面的一些內容,簡單理解下相關知識git

SpringSecurity

Spring Security是一個功能強大、高度可定製的身份驗證和訪問控制框架。它用於保護基於Spring的應用程序。Spring Security是一個專一於向Java應用程序提供身份驗證和受權的框架。與全部Spring項目同樣,Spring安全的真正威力在於它能夠很容易地擴展以知足定製需求。
github

OAuth2.0

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;
    }
}複製代碼

三、操做步驟

不少文章寫的特別複雜,其實主要的內容也就分爲下面幾步安全

3.一、受權服務器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();
    }

}複製代碼

3.二、資源服務器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
    }
}複製代碼

3.三、配置SpringSecurity

@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服務須要簽名才能夠訪問,驗證以下:

  • password模式
    利用postman進行post訪問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"
}複製代碼

  • client模式
    一樣利用postman的POST方式訪問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"
}
複製代碼

對資源進行訪問
  • product服務:訪問http://localhost:8080/product/1獲得以下數據
    product id : 1
  • order服務:訪問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爲最終代碼

github.com/liangliang1…

參考

http://blog.didispace.com/spring-security-oauth2-xjf-1/

相關文章
相關標籤/搜索