認證與授權(quán)(三):搭建授權(quán)、資源服務(wù)器

本篇示例的結(jié)構(gòu)如圖所示:


授權(quán)、資源結(jié)構(gòu)圖.jpg
  1. 客戶端向授權(quán)服務(wù)器請求令牌,授權(quán)服務(wù)器經(jīng)過認證以后,將令牌返回給客戶端。
  2. 客戶端攜帶第1步返回的令牌,向資源服務(wù)器請求資源。
  3. 資源服務(wù)器向授權(quán)服務(wù)器驗證令牌的合法性和有效性。
  4. 若驗證成功,資源服務(wù)器向客戶端返回受限資源。

1、搭建授權(quán)服務(wù)器

授權(quán)服務(wù)器是在上一篇搭建Spring Cloud Security單體應(yīng)用的基礎(chǔ)上完成的。
pom.xml添加依賴

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

Eureka服務(wù)注冊與發(fā)現(xiàn)中心的搭建參考服務(wù)注冊與發(fā)現(xiàn):Eureka
application.yml添加Eureka Client配置

eureka:
    instance:
        instance-id: ${spring.application.name}:${vcap.application.instance_id:${spring. application.instance_id:${random.value}}}
    client:
        service-url:
            default-zone: http://localhost:8761/eureka/

新建AuthorizationServerConfig

package com.ljessie.controlsecurity.config;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
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.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

//開啟授權(quán)服務(wù)器
@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig
        extends AuthorizationServerConfigurerAdapter
{

    @Autowired
    AuthenticationManager manager;

    @Autowired
    PasswordEncoder passwordEncoder;

    /**
     * 用來指定哪些客戶端可以來請求授權(quán)
     * 配置客戶端詳情信息
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        //暫時使用內(nèi)存方式
        //配置一個客戶端,既可以通過授權(quán)碼類型獲取令牌,也可以通過密碼類型獲取令牌
        clients.inMemory().withClient("client") //客戶端ID
                .authorizedGrantTypes("authorization_code","password", "refresh_token") //客戶端可以使用的授權(quán)類型
                .scopes("all") //允許請求范圍
                .secret(passwordEncoder.encode("secret")) //客戶端密鑰
                .redirectUris("http://localhost:8768");//回調(diào)地址
    }

    /**
     * 令牌訪問端點
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(new InMemoryTokenStore())
                .accessTokenConverter(accessTokenConverter())
                .authenticationManager(manager)
                    .reuseRefreshTokens(false);
    }

    /**
     * 配置JWT轉(zhuǎn)換器
     * @return
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("secret");
        return converter;
    }


    /**
     * 令牌端點的安全策略
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                //允許所有人請求令牌
                .tokenKeyAccess("permitAll()")
                //已驗證的客戶端才能請求check_token端點
                .checkTokenAccess("isAuthenticated()")
                //允許表單認證,申請令牌
                .allowFormAuthenticationForClients();
    }
}

2、搭建資源服務(wù)器

首先按照服務(wù)注冊與發(fā)現(xiàn):Eureka中的contro-record搭建Eureka Client
pom.xml然后添加security和oauth依賴

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>

新建TestController,配置一個受限資源接口test_resource

package com.ljessie.controluser.controller;

import com.ljessie.controluser.entity.User;
import com.ljessie.controluser.service.impl.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class TestController {

    @RequestMapping("test_resource")
    public String testResource(){
        return "返回受限資源";
    }

}

新建ResourceServerConfig

package com.ljessie.controluser.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
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;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.web.client.DefaultResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;


@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Autowired
    RestTemplate restTemplate;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources
                .tokenStore(new JwtTokenStore(accessTokenConverter()))
                .stateless(true);
        //配置RemoteTokenServices,用于向認證服務(wù)器驗證令牌
        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        remoteTokenServices.setAccessTokenConverter(accessTokenConverter());

        restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
            /**
             * 忽略400異常
             * @param response
             * @param statusCode
             * @throws IOException
             */
            @Override
            protected void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException {
                if(response.getRawStatusCode() != 400){
                    super.handleError(response);
                }
            }
        });

        remoteTokenServices.setRestTemplate(restTemplate);
        remoteTokenServices.setCheckTokenEndpointUrl("http://authorizationServer/oauth/check_token");
        remoteTokenServices.setClientId("client");
        remoteTokenServices.setClientSecret("secret");

        resources.tokenServices(remoteTokenServices)
                .stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //配置資源服務(wù)器的攔截規(guī)則
        http
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                .and().requestMatchers().anyRequest()
                .and().anonymous()
                //test_resource接口必須經(jīng)過認證才可以訪問
                .and().authorizeRequests().antMatchers("/test_resource").authenticated()
                .and()
                .exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());

    }

    /**
     * 配置JWT轉(zhuǎn)換器
     * @return
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("secret");
        return converter;
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

@EnableResourceServer注解開啟資源服務(wù),configure(ResourceServerSecurityConfigurer resources)方法添加授權(quán)服務(wù)器相關(guān)配置,configure(HttpSecurity http)配置了哪些資源需要授權(quán)才能訪問,accessTokenConverter()配置JWT轉(zhuǎn)換器解析令牌。

3、測試授權(quán)

依次啟動control-eureka-server、control-security和control-user。
使用postman訪問:http://localhost:8762/test_resource,返回未授權(quán)。

未授權(quán).jpg

訪問請求令牌接口,請求方式POST:http://localhost:8766/oauth/token?username=066111&password=123456&grant_type=password&scope=all&client_id=client&client_secret=secret
返回令牌.jpg

請求令牌的接口是Spring Cloud Security自帶的,不需要我們自己手寫。
然后復(fù)制access_token,再次訪問首先資源接口:
返回資源.jpg

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容