在做项目的时候,用户身份认证最开始用的是web拦截器来实现的,原理很简单,可以参考这篇文章:springboot拦截器配置_酷乐丶是只猫的博客-CSDN博客
使用Eureka后可以将用户身份认证功能单独从项目中抽取出来,成为一个独立的模块,降低了项目的耦合性,同时也方便其他项目访问该功能。
1.Eureka服务中心部署
搭建Eureka项目首先需要搭建一个服务中心,服务中心相当于一个中转站,其中包含了各种服务,服务之间可以相互访问。
首先新建一个spring项目
服务器中心的pom.xml依赖如下(注意spring cloud与springboot的版本对应关系,如果springboot版本过高或者过低,都需要调整spring cloud的版本,这里用的springboot的版本是2.6.1,对应的spring cloud版本是2021.0.0):
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
接着再配置一下yml文件。
eureka.client.registerWithEureka 表示是否将自己注册到Eureka服务中心,因为其本身就是个服务中心,因此设为false。
eureka.client.fetchRegistry 表示是否从Eureka服务中心获取注册信息,同样也是设为false。
eureka.client.serviceUrl.defaultZone :设置与Eureka服务中心交互地址,查询服务和注册服务都需要依赖这个地址。
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
然后配置下spring启动类,添加一个@EnableEurekaServer注解即可:
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
2.网关服务部署
部署完服务中心后,需要将网关服务注册进服务中心,这里用到的是spring cloud gateway。
同样先新建一个spring项目。
pom.xml依赖如下
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--开启健康检查-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--spring-cloud-starter-gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
接着配置yml文件,这里配置了gateway的属性,discovery开启服务注册中心服务发现,routes为网关路由配置,当通过网关认证时,页面会跳转到url中的路径;predicates则会对匹配的请求进行过滤验证;filters为过滤器配置,JwtAuthorization为过滤器名字:
server:
port: 8877
spring:
application:
name: microservice-eureka-user
cloud:
gateway:
discovery:
locator:
enabled: true #开启 Gateway 服务注册中心服务发现
lower-case-service-id: true
routes:
- id: api
uri: http://localhost:8888
predicates:
- Path=/api/**
filters:
- StripPrefix=0
- JwtAuthorization
eureka:
instance:
prefer-ip-address: true
instance-id: ${spring.cloud.client.ip-address}:${server.port}
ip-address: ${spring.cloud.client.ip-address}
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka
healthcheck:
enabled: true
filter工厂配置:
@Component
public class JwtAuthorizationGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
@Override
public GatewayFilter apply(Object config) {
return new JwtAuthorizationFilter();
}
}
filter配置:
@Component
public class JwtAuthorizationFilter implements GatewayFilter, Ordered {
List<String> ignoreList = Arrays.asList("/api/loginPage/login");
/**
* 该值要和auth-server中配置的签名相同
* <p>
* com.kdyzm.spring.security.auth.center.config.TokenConfig#SIGNING_KEY
*/
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
boolean access = true;
ServerHttpRequest serverReques = exchange.getRequest();
// 将Payload数据放到header
ServerHttpRequest.Builder builder = serverReques.mutate();
if(!access){
return unAuthorized(exchange,"认证不通过",null);
}
// 继续执行
return chain.filter(exchange.mutate().request(builder.build()).build());
}
private Mono<Void> unAuthorized(ServerWebExchange exchange, String msg,Integer status) {
try {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//这里需要指定响应头部信息,否则会中文乱码
exchange.getResponse().getHeaders().add("Content-Type", "application/json;charset=UTF-8");
JSONObject ret=new JSONObject();
if(Objects.isNull(status)) {
ret.put("code", 401);
ret.put("message", msg);
ret.put("data",null);
}else{
ret.put("code", status);
ret.put("message", msg);
ret.put("data",null);
}
String s = objectMapper.writeValueAsString(ret);
DataBuffer buffer = exchange
.getResponse()
.bufferFactory()
.wrap(s.getBytes(StandardCharsets.UTF_8));
return exchange.getResponse().writeWith(Flux.just(buffer));
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将该过滤器的优先级设置为最高,因为只要认证不通过,就不能做任何事情
*
* @return
*/
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
当请求进行过滤时会先进入filter方法,该方法可以设置一些请求认证的功能,当请求通过认证时,返回chain.filter继续执行,否则返回unAuthorized方法拒绝通过网关。