springcloud项目搭建
springcloud项目搭建
搭建父工程
首先先创建父工程,删除src文件夹,并在pom中进行子项目的版本控制
<dependencyManagement>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR8</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.3.RELEASE</version>
</plugin>
</plugins>
</build>
搭建eureka
maven
右键单击父工程–>new–>modules,新建eureka工程
maven核心依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
yml文件
resource下新建application.yml文件
spring:
profiles:
active: default
---
server:
port: 8761 #指定运行端口
spring:
application:
name: eureka-server #指定服务名称
profiles: default
eureka:
instance:
hostname: localhost #指定主机地址
client:
fetch-registry: false #指定是否要从注册中心获取服务(默认true)
register-with-eureka: false #指定是否要注册到注册中心(默认true)
server:
enable-self-preservation: false #是否开启保护模式(默认true)
eviction-interval-timer-in-ms: 3000 # 清理间隔(单位毫秒,默认是60*1000)
logging:
level:
com.netflix: warn
---
spring:
application:
name: eureka-cluster #指定服务名称
profiles: eureka1
server:
port: 8011 #指定运行端口
eureka:
instance:
hostname: localhost #指定主机地址
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
register-with-eureka: true #指定是否要注册到注册中心(默认true)
fetch-registry: true #指定是否要从注册中心获取服务(默认true)
serviceUrl:
defaultZone: http://localhost:8012/eureka/,http://localhost:8013/eureka/ #注册到另一个Eureka注册中心
server:
enable-self-preservation: false #是否开启保护模式(默认true)
eviction-interval-timer-in-ms: 3000 # 清理无效服务节点的时间间隔(单位毫秒,默认是60*1000)
logging:
level:
com.netflix: warn
---
spring:
application:
name: eureka-cluster #指定服务名称
profiles: eureka2
server:
port: 8012 #指定运行端口
eureka:
instance:
hostname: localhost #指定主机地址
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
register-with-eureka: true #指定是否要注册到注册中心(默认true)
fetch-registry: true #指定是否要从注册中心获取服务(默认true)
serviceUrl:
defaultZone: http://localhost:8011/eureka/,http://localhost:8013/eureka/ #注册到另一个Eureka注册中心
server:
enable-self-preservation: false #是否开启保护模式(默认true)
eviction-interval-timer-in-ms: 3000 # 清理无效服务节点的时间间隔(单位毫秒,默认是60*1000)
logging:
level:
com.netflix: warn
---
spring:
application:
name: eureka-cluster #指定服务名称
profiles: eureka3
server:
port: 8013 #指定运行端口
eureka:
instance:
hostname: localhost #指定主机地址
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
register-with-eureka: true #指定是否要注册到注册中心(默认true)
fetch-registry: true #指定是否要从注册中心获取服务(默认true)
serviceUrl:
defaultZone: http://localhost:8012/eureka/,http://localhost:8011/eureka/ #注册到另一个Eureka注册中心
server:
enable-self-preservation: false #是否开启保护模式(默认true)
eviction-interval-timer-in-ms: 3000 # 清理无效服务节点的时间间隔(单位毫秒,默认是60*1000)
logging:
level:
com.netflix: warn
启动类
新建EurekaApplication启动类,在类上添加@EnableEurekaServer注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
测试
启动eureka,启动成功后访问http://localhost:8761/,出现以下界面则eureka配置成功
zuul网关
maven
新建zuul子工程,添加maven核心依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
将其加入eureka服务中心
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
yml文件
spring:
application:
name: zuul-proxy #服务名
server:
port: 10000 # 端口
#注册中心相关配置
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
instance-id: ${spring.cloud.client.ip-address}:${server.port}
#网关配置
zuul:
# LogFilter: #自定义过滤器的名称
# pre: #自定义过滤器类型
# disable: true # 是否禁用
prefix: /api/ #前缀
routes: #路由规则
user:
path: /u/**
url: http://localhost:8102 #远程服务必须有url
permission:
path: /p/**
student:
path: /s/**
# 关闭那些服务的默认路由
ignored-services: permission,user,student
add-host-header: true
#不对敏感资源做拦截
sensitive-headers:
ignored-headers:
host:
socket-timeout-millis: 4000 # 请求的处理时间
connect-timeout-millis: 4000 # 请求的链接时间
#开启路由端点
management:
endpoints:
web:
exposure:
include: 'routes'
ZuulApplication
类上添加@EnableDiscoveryClient,@EnableZuulProxy注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableDiscoveryClient
@EnableZuulProxy
@SpringBootApplication
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class,args);
}
}
启动ZuulApplication,启动完成后刷新eureka
显示如图则配置成功
config配置中心(需要gitee)
config服务端
maven
新建config子工程,添加核心依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
如果要使用消息总线则添加rabbitMQ依赖
<!--配置rabbitMQ开始-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
yml文件
server:
port: 8901
spring:
application:
name: config-server
rabbitmq: #rabbitmq相关配置
host: localhost
port: 5672
username: guest
password: guest
cloud:
config:
server:
git: #配置存储配置信息的Git仓库
uri: https://gitee.com/wxkjpub/config-zhuchunsheng.git
username: wxkjpub
password: wxkj@123456
clone-on-start: true #配置中心启动时直接从git获取配置
eureka:
instance:
hostname: localhost #指定主机地址
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
service-url:
defaultZone: http://localhost:8761/eureka/
management:
endpoints: #暴露bus刷新配置的端点
web:
exposure:
include: 'bus-refresh'
启动类ConfigApplication
类上添加@EnableDiscoveryClient、@EnableConfigServer注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;
@EnableDiscoveryClient
@EnableConfigServer
@SpringBootApplication
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class,args);
}
}
启动ConfigApplication ,刷新eureka
被配置中心管理yml文件的项目
maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
添加额外yml配置
添加单个服务的刷新
#刷新配置
management:
endpoints:
web:
exposure:
include: 'refresh'
添加配置中心的的地址
spring:
application:
name: zuul
cloud:
#Config客户端配置
config:
#启用配置后缀名称
profile: dev
#分支名称
label: master
#配置中心地址
uri: http://localhost:8901
#配置文件名称
name: zuul
security
新建security子项目,并添加核心依赖
maven
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
application.yml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://192.168.0.142:3306/springcloud?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
application:
name: security
eureka:
instance:
lease-renewal-interval-in-seconds: 5
#续约更新时间间隔
lease-expiration-duration-in-seconds: 15
#续约更新时间间隔
hostname: localhost
#指定主机地址
instance-id: ${spring.cloud.client.ip-address}:${server.port}
client:
healthcheck:
enabled: true
register-with-eureka: true
#注册到Eureka的注册中心
fetch-registry: true
#获取注册实例列表
service-url:
defaultZone: http://localhost:8761/eureka
#配置注册中心地址
registry-fetch-interval-seconds: 10
# 设置服务消费者从注册中心拉取服务列表的间隔
jwt:
#定义我们的盐 密码
secret: mySecret
#过期时间
expiration: 1
#token 的类型 说明他以 bearer 开头
tokenHead: bearer
#token 对应的 key
tokenHeader: Authorization
# {Authorization: "bearer sdfdsfsdfsdfdsfsdfadfdsf"}
SecurityApplication
@SpringBootApplication
@EnableDiscoveryClient
public class SecurityApplication {
public static void main(String[] args) {
SpringApplication.run(SecurityApplication.class,args);
}
}
生成权限验证的token类
JwtTokenUtil
package com.wanxi.spring.cloud.security.commons;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.stream.Collectors;
/**
* JwtToken生成的工具类
* JWT token的格式:header.payload.signature
* header的格式(算法、token的类型):
* {"alg": "HS512","typ": "JWT"}
* payload的格式(用户名、创建时间、生成时间):
* {"sub":"wang","created":1489079981393,"exp":1489684781}
* signature的生成算法:
* HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
* Created by wx on 2019/4/26.
*/
@Component
public class JwtTokenUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(JwtTokenUtil.class);
private static final String CLAIM_KEY_USERNAME = "sub";
private static final String CLAIM_KEY_CREATED = "created";
private static final String CLAIM_KEY_AUTHORITY = "auth";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
/**
* 根据负责生成JWT的token
*/
private String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/**
* 从token中获取JWT中的负载
*/
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
LOGGER.info("JWT格式验证失败:{}",token);
}
return claims;
}
/**
* 生成token的过期时间
*/
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
/**
* 从token中获取登录用户名
*/
public String getUserNameFromToken(String token) {
String username;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 验证token是否还有效
* @param token 客户端传入的token
* @param userDetails 从数据库中查询出来的用户信息
*/
public boolean validateToken(String token, UserDetails userDetails) {
String username = getUserNameFromToken(token);
return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
}
/**
* 判断token是否已经失效
*/
private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date());
}
/**
* 从token中获取过期时间
*/
private Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token);
return claims.getExpiration();
}
/**
* 根据用户信息生成token
*/
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
Set<Object> collect = authorities.stream().filter(p -> StringUtils.hasText(p.getAuthority()))
.map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
claims.put(CLAIM_KEY_CREATED, new Date());
claims.put(CLAIM_KEY_AUTHORITY,collect);
return generateToken(claims);
}
/**
* 判断token是否可以被刷新
*/
public boolean canRefresh(String token) {
return !isTokenExpired(token);
}
/**
* 刷新token
*/
public String refreshToken(String token) {
Claims claims = getClaimsFromToken(token);
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
}
设置跨域
FilterConfig
package com.wanxi.spring.cloud.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
@Order(1)
public class FilterConfig {
private CorsConfiguration buildConfig(){
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedHeader("*"); // 允许任何的head头部
corsConfiguration.addAllowedOrigin("*"); // 允许任何域名使用
corsConfiguration.addAllowedMethod("*"); // 允许任何的请求方法
corsConfiguration.setAllowCredentials(true);
return corsConfiguration;
}
// 添加CorsFilter拦截器,对任意的请求使用
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
}
security配置类
package com.wanxi.spring.cloud.security.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http .cors().and()
.csrf().disable()
.sessionManagement()// 基于token,所以不需要 securityContext
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/fonts/**","/user/login","/user/login2").permitAll() //都可以访问
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated() // 任何请求都需要认证
.and()
.userDetailsService(userDetailsService)
;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
至此搭建基本完成