SpringCloud - Spring Cloud 之 Security服务安全机制(二十)

Posted MinggeQingchun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud - Spring Cloud 之 Security服务安全机制(二十)相关的知识,希望对你有一定的参考价值。

阅读本文前可先参考

​​​​​​SpringCloud - Spring Cloud根/父项目,开发准备(二)_MinggeQingchun的博客-CSDN博客

Spring Security

微服务的Rest服务都是基于http请求的,因此很有可能暴露在公网上,任何人都可能调用访问,如果Rest服务有一些私密信息,就会导致信息的泄露,因此我们需要给微服务增加安全机制。如:Spring Security

Spring Security 是 Spring Resource 社区的一个安全组件, Spring Security 为 JavaEE 企业开发提供了全面的安全防护。

Spring Security 采用“安全层”的概念,使每一层都尽可能安全,连续的安全层可以达到全面的防护

Spring Seeurity 可以在 Controller 层、 Service 层、 DAO 层等以加注解的方式来保护应用程序的安全。

Spring Security 提供了细粒度的权限控制,可以精细到每一个 API 接口、每一个业务的方法,或者每一个操作数据库的 DAO 层的方法。

Spring Security 提供的是应用程序层的安全解决方案,一个系统的安全还需要考虑传输层和系统层的安全,例如采用 Htpps 协议、服务器部署防火墙等。

Spring Security 是一个提供身份验证、授权和针对常见攻击的保护的框架。凭借对保护命令式和反应式应用程序的一流支持,它是保护基于 Spring 的应用程序的事实上的标准

一、服务提供者集成 Security 安全认证

1、创建一个 springboot Module 服务提供者 springcloud-11-service-security-provider

  

2、添加 spring-boot-starter-security等 依赖

<!--spring-boot-starter-security-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--继承统一的父项目-->
    <parent>
        <groupId>com.company</groupId>
        <artifactId>springcloud-demo</artifactId>
        <version>1.0.0</version>
    </parent>

    <groupId>com.company</groupId>
    <artifactId>springcloud-11-service-security-provider</artifactId>
    <version>1.0.0</version>

    <name>springcloud-11-service-security-provider</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--spring web 起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- mysql的jdbc驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!--mybatis起步依赖-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>

        <!--依赖统一的springcloud-service-commons项目-->
        <dependency>
            <groupId>com.company</groupId>
            <artifactId>springcloud-2-service-common</artifactId>
            <version>1.0.0</version>
        </dependency>

        <!-- springboot 开发自动热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

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

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

    </dependencies>

    <build>

        <!--处理资源目录-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>

            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>

        <plugins>

            <!--spring boot提供的编译、打包的Maven插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

3、application.properties配置文件中添加安全认证账号密码

#安全认证;配置访问账号和密码
spring.security.user.name=admin
spring.security.user.password=123456
server.port=9001

#设置应用名称,对应Eureka控制台下 DS Replicas 的 Application
spring.application.name=springcloud-11-service-security-provider

#设置mysql数据库连接信息
spring.datasource.url=jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=admin123456

#每间隔5s,向Eureka服务注册中心发送一次心跳,证明服务是否依然“存活”
eureka.instance.lease-renewal-interval-in-seconds=5
#告诉服务端,如果10s之内没有发送心跳,就代表故障,将本服务踢出
eureka.instance.lease-expiration-duration-in-seconds=10
#告诉服务端,服务实例以IP作为链接,不是取机器名
eureka.instance.prefer-ip-address=false

#注册服务实例名称
eureka.instance.instance-id=springcloud-11-service-security-provider
#注册中心的链接地址
eureka.client.service-url.defaultZone=http://localhost:8761/eureka


#安全认证;配置访问账号和密码
spring.security.user.name=admin
spring.security.user.password=123456

4、创建 controller,mapper,model等文件基于RESTFUL风格访问

@RestController
public class GoodsController 

    @Autowired
    private GoodsService goodsService;

    @GetMapping(value = "/eureka/security/goodList")
    public List<Goods> goodList()
        List<Goods> goodsList = goodsService.getAllGoods();

        System.out.println("查询商品列表成功:");
        for (Goods good:goodsList) 
            System.out.println("查询商品:"+ good);
        
        return goodsList;
    

5、启动springboot 启动类,输入 http://localhost:9001/eureka/security/goodList

访问,首先会进入安全登录页,输入账号密码即可访问

注:

在这里,遇到一个坑,因为刚开始复制的 Sleuth + Zipkin 模块的项目,因此运行 导致只能访问一次,应该是 Spring CLoud内部版本之间有点冲突,去掉 Sleuth + Zipkin 依赖即可

二、服务消费者集成 Security 安全认证

当远程的服务提供者使用了 密码安全验证,此时服务的消费方如果想直接访问服务提供者就不能访问,需要对服务消费者 进行配置处理

1、RestTemplate 调用

1、创建一个 springboot Module 服务消费者 springcloud-11-service-security-consumer

2、pom.xml文件如下:

<!--继承统一的父项目-->
    <parent>
        <groupId>com.company</groupId>
        <artifactId>springcloud-demo</artifactId>
        <version>1.0.0</version>
<!--        <relativePath/> &lt;!&ndash; lookup parent from repository &ndash;&gt;-->
    </parent>

    <groupId>com.company</groupId>
    <artifactId>springcloud-11-service-security-consumer</artifactId>
    <version>1.0.0</version>

    <name>springcloud-11-service-security-consumer</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--spring web 起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--依赖统一的springcloud-service-commons项目-->
        <dependency>
            <groupId>com.company</groupId>
            <artifactId>springcloud-2-service-common</artifactId>
            <version>1.0.0</version>
        </dependency>

        <!-- springboot 开发自动热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

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

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

3、application.properties配置文件

server.port=8081

#设置应用名称,对应Eureka控制台下 DS Replicas 的 Application
spring.application.name=springcloud-11-service-security-consumer

#每间隔5s,向Eureka服务注册中心发送一次心跳,证明服务是否依然“存活”
eureka.instance.lease-renewal-interval-in-seconds=30
#告诉服务端,如果10s之内没有发送心跳,就代表故障,将本服务踢出
eureka.instance.lease-expiration-duration-in-seconds=60
#告诉服务端,服务实例以IP作为链接,不是取机器名
eureka.instance.prefer-ip-address=false

#注册服务实例名称
eureka.instance.instance-id=springcloud-11-service-security-consumer
#注册中心的链接地址
#eureka.client.service-url.defaultZone=http://localhost:8761/eureka
eureka.client.service-url.defaultZone=http://admin:123456@localhost:8761/eureka

4、 在RestConfig配置类中添加 HttpHeaders(org.springframework.http.HttpHeaders)

@Configuration
public class RestConfig 

    //使用Ribbon实现负载均衡的调用
    @LoadBalanced
    @Bean
    public RestTemplate restTemplate () 
        return new RestTemplate();
    

    /**
     * 进行 Http头信息配置实现安全认证
     */
    @Bean
    public HttpHeaders getHeaders()
        //定义 HTTP 头部信息
        HttpHeaders headers = new HttpHeaders();
        /*
        认证的账户密码
        服务提供者 application.properties 配置文件中 安全认证配置的访问账号和密码
        spring.security.user.name=admin
        spring.security.user.password=123456
        * */
        String auth = "admin:123456";

        //加密处理
        byte[] encodedAuth = Base64.getEncoder().encode(auth.getBytes(Charset.forName("US-ASCII")));
        String authHeader = "Basic " + new String(encodedAuth);
        headers.set("Authorization", authHeader);
        return headers;
    

5、在controller中调用的地方注入该HttpHeaders,且传入这个http头信息

@RestController
public class GoodsController 
    private final String GOODS_SERVICE_URL = "http://localhost:9001/service/goodList";

    private final String GOODS_SERVICE_EUREKA_URL = "http://springcloud-11-service-security-provider/eureka/security/goodList";

    @Autowired
    private RestTemplate restTemplate;

    //HttpHeaders这个bean注入到controller中
    @Autowired
    private HttpHeaders httpHeaders;

    @GetMapping(value = "/springcloud/security/goodList")
    public @ResponseBody Object getGoodList()
        //调用远程的一个controller(Restful风格调用)
        ResponseEntity<Object> responseEntity = restTemplate.exchange(GOODS_SERVICE_EUREKA_URL, HttpMethod.GET, new HttpEntity<Object>(httpHeaders),Object.class);

        return responseEntity.getBody();
    

6、依次启动Eureka,服务提供者,服务消费者 访问

http://localhost:8081/springcloud/security/goodList

注:

eureka中也配置了安全认证 

eureka中application.properties


#指定服务注册中心的位置  eureka.client.service-url.defaultZone=http://localhost:8761/eureka
#eureka.client.service-url.defaultZone=http://localhost:8761/eureka
eureka.client.service-url.defaultZone=http://admin:123456@localhost:8761/eureka


#安全认证;配置访问config配置中心的访问账号和密码
spring.security.user.name=admin
spring.security.user.password=123456

提供者中

eureka.client.service-url.defaultZone=http://admin:123456@localhost:8761/eureka


#安全认证;配置访问账号和密码
spring.security.user.name=admin
spring.security.user.password=123456

消费者中

#注册服务实例名称
eureka.instance.instance-id=springcloud-11-service-security-consumer
#注册中心的链接地址
#eureka.client.service-url.defaultZone=http://localhost:8761/eureka
eureka.client.service-url.defaultZone=http://admin:123456@localhost:8761/eureka

2、OpenFeign 调用

1、 在 springcloud-2-service-common 公共模块新增一个 FeignConfiguration 配置类

@Configuration
public class FeignConfiguration 

    //@RequestLine("GET /eureka/security/goodList")

    /**
     * 一种契约,采用feign的契约方式,如果不配置该bean,会转成SpringMVC的方式
     */
    /*@Bean
    public Contract feignContract()
        return new Contract.Default();
    */

    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor()
        //传用户名和密码
        return new BasicAuthRequestInterceptor("admin","123456");
    

2、 新增一个 服务声明接口 ProviderSecurityGoodsRemoteClient 

@Component
@FeignClient(value = "springcloud-11-service-security-provider",
        /*fallback = HystrixProviderGoodsRemoteClientFallBack.class,*/
        fallbackFactory = HystrixProviderGoodsRemoteClientFallBackFactory.class,
        configuration = FeignConfiguration.class)
public interface ProviderSecurityGoodsRemoteClient 

    /**
     * 声明一个feign的接口,它的实现是服务提供者的controller实现
     */
    @GetMapping(value = "/eureka/security/goodList")
    public List<Goods> goods();

3、服务消费者引入依赖

<dependencies>
        <!--spring web 起步依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--依赖统一的springcloud-service-commons项目-->
        <dependency>
            <groupId>com.company</groupId>
            <artifactId>springcloud-2-service-common</artifactId>
            <version>1.0.0</version>
        </dependency>

        <!-- springboot 开发自动热部署 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>

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

    </dependencies>

4、消费者启动类添加注解

@EnableFeignClients  //表示开启 Spring Cloud OpenFeign的支持功能
@EnableEurekaClient  //开启 Eureka client服务
@SpringBootApplication
public class Security11ConsumerApplication 
    public static void main(String[] args) 
        SpringApplication.run(Security11ConsumerApplication.class, args);
    

5、消费者controller调用

@RestController
public class GoodsController 

    @Autowired
    private ProviderSecurityGoodsRemoteClient providerSecurityGoodsRemoteClient;


    @GetMapping(value = "/springcloud/security/openfeign/goodList")
    public @ResponseBody Object getGoodListOpenFeign()

        // 调用远程的一个controller, restful的调用,通过openfeign这种声明式的远程调用,providerGoodsRemoteClient就像dubbo里面的接口层一样
        return  providerSecurityGoodsRemoteClient.goods();
    

浏览器输入http://localhost:8081/springcloud/security/openfeign/goodList

注:

在这里,遇到一个坑,因为刚开始复制的 Sleuth + Zipkin 模块的项目,因此运行 导致只能访问一次,应该是 Spring CLoud内部版本之间有点冲突,去掉 Sleuth + Zipkin 依赖即可

三、安全服务模块/项目

在实际项目开发中,服务一般会非常多,绝大多数服务都需要用到安全验证,账户密码基本一样,如果每个服务都单独配置安全认证,繁琐重复劳动不是我们提倡的,因此单独建立一个安全服务验证的项目,其他微服务如果需要安全认证就引入该项目的依赖即可

1、新建一个模块 springcloud-11-service-security-auth

2、添加 spring-boot-starter-security 依赖

<!--继承统一的父项目-->
    <parent>
        <groupId>com.company</groupId>
        <artifactId>springcloud-demo</artifactId>
        <version>1.0.0</version>
    </parent>

    <groupId>com.company</groupId>
    <artifactId>springcloud-11-service-security-auth</artifactId>
    <version>1.0.0</version>

    <name>springcloud-11-service-security-auth</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <!--spring-boot-starter-security-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

3、新增配置类 WebSecurityConfiguration

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter 
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception
        auth.inMemoryAuthentication()
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("USER","ADMIN")
                .and()
                .withUser("root")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("USER","ROOT");
    

    @Override
    public void configure(HttpSecurity httpSecurity) throws Exception
        httpSecurity.httpBasic().and().authorizeRequests().anyRequest().fullyAuthenticated();
        httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //把csrf拦截置为不可用,401问题
        httpSecurity.csrf().disable();
    

4、在服务提供者模块中引入本模块依赖,启动运行测试即可

<!--微服务统一安全认证依赖-->
        <dependency>
            <groupId>com.company</groupId>
            <artifactId>springcloud-11-service-security-auth</artifactId>
        </dependency>

 这里的UI界面会和之前有些区别

1、之前界面是spring封装的security

2、当前是使用的security自己的jar包

以上是关于SpringCloud - Spring Cloud 之 Security服务安全机制(二十)的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud总结29.Zuul的FallBack回退机制

基于consul的服务注册(含踩坑过程)

Oauth2.0 整合springCloud的Zuul 解决关键BUG 报错信息:Principal must not be null

Spring Cloud Alibaba - 02 SpringCloud SpringCloud Alibaba SpringBoot的生产版本选择

Spring Cloud

spring cloud 服务注册、发现了解