赞
踩
Spring Cloud Gateway + Spring Oauth 2.0 整合(服务端与资源端分离)
个人开发环境
java环境:Jdk1.8.0_60 (idea 需安装lombok插件)
编译器:IntelliJ IDEA 2019.1
框架:spirng cloud Hoxton + springboot 2.2 + spring oauth 2.0 + spring security 5
一、前言
服务名
注释
描述
yoci-auth
鉴权服务
实现一个简单的基本的 oauth2鉴权服务 使用 jwt token,使用自定义 JwtTokenStore
yoci-api
资源服务
实现简单资源服务,提供简单的 Restful API,通过 gateway调用
yoci-gate
网关服务
使用 spring cloud gateway 实现简单路由,实现统一路由转发
依次运行 yoci-auth,yoci-gate,yoci-api
二、父工程构建
1.pom.xml
UTF-8
UTF-8
1.8
1.8
1.8
Hoxton.SR1
Cairo-SR8
2.1.1.RELEASE
2.2.2.RELEASE
yoci-auth
yoci-api
yoci-gate
org.springframework.boot
spring-boot-dependencies
${spring-boot.version}
pom
import
io.spring.platform
platform-bom
${spring-platform.version}
pom
import
org.springframework.cloud
spring-cloud-dependencies
${spring-cloud.version}
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
${alibaba-cloud.version}
pom
import
org.projectlombok
lombok
1.16.14
provided
oss
oss
https://oss.sonatype.org/content/groups/public
spring-milestones
Spring Milestones
https://repo.spring.io/libs-milestone
false
sonatype-nexus-snapshots
Sonatype Nexus Snapshots
https://oss.sonatype.org/content/repositories/snapshots/
三、网关
网关在demo中实现统一路由转发作用,不做安全验证,在实战微服务环境中,也可在gateway网关处实现其提供的相应过滤器进行统一拦截,实现安全验证,此demo为了学习演示方便,采用资源服务器通过远程token校验进行安全验证
1.pom.xml
yoci-gate
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-starter-webflux
org.springframework.cloud
spring-cloud-starter-gateway
2.application.yml
server:
port: 8082
## gateway
spring:
application:
name: yoci-gate
cloud:
gateway:
discovery:
locator:
enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由
routes:
- id: yoci-api
# uri: lb://yoci-api # 动态路由方式需要配合eureka、nacos注册中心使用
uri: http://localhost:8083
predicates:
- Path=/api/**
filters:
- StripPrefix=1
- id: yoci-auth
# uri: lb://yoci-auth # 动态路由方式需要配合eureka、nacos注册中心使用
uri: http://localhost:8081
predicates:
- Path=/auth/**
filters:
- StripPrefix=1
3.启动类
/**
* 网关主启动类
*
* @author: YoCiyy
* @date: 2020/6/22
*/
@SpringBootApplication
public class GateBootstrap {
public static void main(String[] args) {
SpringApplication.run(GateBootstrap.class, args);
}
}
四、服务端(鉴权服务 yoci-auth)
1.pom.xml
yoci-auth
org.springframework.cloud
spring-cloud-starter-oauth2
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-starter-web
2.application.yml
# 服务端口号
server:
port: 8081
# 服务名
spring:
application:
name: yoci-auth
# actuator
management:
endpoints:
web:
exposure:
include: "*"
3.启动类
/**
* auth主启动类
*
* @author: YoCiyy
* @date: 2020/6/22
*/
@SpringBootApplication
public class AuthBootstrap {
public static void main(String[] args) {
SpringApplication.run(AuthBootstrap.class, args);
}
}
4.java配置类
创建oauth认证服务器配置AuthorizationServerConfig 配置类,继承AuthorizationServerConfigurerAdapter
/**
* oauth认证服务器配置
*
* @author: YoCiyy
* @date: 2020/6/22
*/
@Configuration
@AllArgsConstructor
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
/** 令牌持久化配置 */
private final TokenStore tokenStore;
/** 客户端详情服务 */
private final ClientDetailsService clientDetailsService;
/** 认证管理器 */
private final AuthenticationManager authenticationManager;
/** 授权码服务 */
private final AuthorizationCodeServices authorizationCodeServices;
/** jwtToken解析器 */
private final JwtAccessTokenConverter jwtAccessTokenConverter;
/**
* 客户端详情服务配置 (demo采用本地内存存储)
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
// 使用本地内存存储
.inMemory()
// 客户端id
.withClient("client_1")
// 客户端密码
.secret(new BCryptPasswordEncoder().encode("123456"))
// 该客户端允许授权的类型
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
// 该客户端允许授权的范围
.scopes("all")
// false跳转到授权页面,true不跳转,直接发令牌
.autoApprove(false);
}
/**
* 配置访问令牌端点
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
// 认证管理器
.authenticationManager(authenticationManager)
// 授权码服务
.authorizationCodeServices(authorizationCodeServices)
// 令牌管理服务
.tokenServices(tokenServices())
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
/**
* 配置令牌端点安全约束
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security
// oauth/check_token公开
.checkTokenAccess("permitAll()")
// oauth/token_key 公开密钥
.tokenKeyAccess("permitAll()")
// 允许表单认证
.allowFormAuthenticationForClients();
}
/**
* 令牌服务配置
*
* @return 令牌服务对象
*/
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore);
tokenServices.setSupportRefreshToken(true);
// 令牌增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
tokenServices.setTokenEnhancer(tokenEnhancerChain);
// 令牌默认有效期2小时
tokenServices.setAccessTokenValiditySeconds(7200);
// 刷新令牌默认有效期3天
tokenServices.setRefreshTokenValiditySeconds(259200);
return tokenServices;
}
}
创建Security 安全配置类WebSecurityConfig 继承WebSecurityConfigurerAdapter
/**
* security 安全相关配置类
*
* @author: YoCiyy
* @date: 2020/6/22
*/
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 安全拦截机制
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
// 放行
.antMatchers("/auth/**")
.permitAll()
// 其他请求必须认证通过
.anyRequest().authenticated()
.and()
.formLogin() // 允许表单登录
// .successForwardUrl("/login-success") //自定义登录成功跳转页
.and()
.csrf().disable();
}
/**
* token持久化配置
*/
@Bean
public TokenStore tokenStore() {
// 本地内存存储令牌
return new InMemoryTokenStore();
}
/**
* 密码加密器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 认证管理器配置
*/
@Bean
@Override
protected AuthenticationManager authenticationManager() {
return authentication -> daoAuthenticationProvider().authenticate(authentication);
}
/**
* 认证是由 AuthenticationManager 来管理的,但是真正进行认证的是 AuthenticationManager 中定义的 AuthenticationProvider,用于调用userDetailsService进行验证
*/
@Bean
public AuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService());
daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
return daoAuthenticationProvider;
}
/**
* 用户详情服务
*/
@Bean
@Override
protected UserDetailsService userDetailsService() {
// 测试方便采用内存存取方式
InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
userDetailsService.createUser(User.withUsername("user_1").password(passwordEncoder().encode("123456")).authorities("ROLE_USER").build());
userDetailsService.createUser(User.withUsername("user_2").password(passwordEncoder().encode("1234567")).authorities("ROLE_USER").build());
return userDetailsService;
}
/**
* 设置授权码模式的授权码如何存取,暂时采用内存方式
*/
@Bean
public AuthorizationCodeServices authorizationCodeServices() {
return new InMemoryAuthorizationCodeServices();
}
/**
* jwt token解析器
*/
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
// 对称密钥,资源服务器使用该密钥来验证
converter.setSigningKey("YoCiyy");
return converter;
}
}
5.postman测试
采用密码模式访问测试
五、资源端(资源服务yoci-api)
1.pom.xml
yoci-api
org.springframework.cloud
spring-cloud-starter-oauth2
org.springframework.boot
spring-boot-starter-actuator
org.springframework.boot
spring-boot-starter-web
2.application.yml
server:
port: 8083
spring:
application:
name: yoci-api
3.启动类
/**
* 模拟接口启动类
*
* @author: YoCiyy
* @date: 2020/6/23
*/
@SpringBootApplication
public class ApiBootstrap {
public static void main(String[] args) {
SpringApplication.run(ApiBootstrap.class, args);
}
}
4.java配置类
创建资源服务配置ResourceServerConfig继承ResourceServerConfigurerAdapter
/**
* 资源服务配置
*
* @author: YoCiyy
* @date: 2020/6/19
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
/**
* token服务配置
*/
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenServices());
}
/**
* 路由安全认证配置
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// 配置hello打头的路由需要安全认证,order无配置无需认证
.antMatchers("/hello/**").authenticated()
.and().csrf().disable();
}
/**
* jwt token 校验解析器
*/
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
/**
* Token转换器必须与认证服务一致
*/
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("YoCiyy");
return accessTokenConverter;
}
/**
* 资源服务令牌解析服务
*/
@Bean
@Primary
public ResourceServerTokenServices tokenServices() {
RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:8081/oauth/check_token");
remoteTokenServices.setClientId("client_1");
remoteTokenServices.setClientSecret("123456");
return remoteTokenServices;
}
}
5.postman测试
通过网关路由转发,请求资源服务器
不配置请求头token,直接/order可直接访问成功(在资源服务配置类中,/order打头路由没有配置安全路由)
不配置请求头中的token,访问/hello报401错误
请求头中配置申请到的token,格式 Bearer (申请到的token),访问/hello测试调用资源服务api成功
相关学习资料
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。