20.SpringCloud - Spring Cloud 之 Security服务安全机制(二十)
20.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/> <!– lookup parent from repository –>-->
</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包
来源:https://blog.csdn.net/MinggeQingchun/article/details/125496003