Springcloud gateway网关+认证服务+token方式,入口层认证统一微服务鉴权【设计实践】

这篇具有很好参考价值的文章主要介绍了Springcloud gateway网关+认证服务+token方式,入口层认证统一微服务鉴权【设计实践】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

背景

实现

gateway

maven配置

yml配置

页面登录拦截配置类

白名单配置

token工具类

登录配置类

全局过滤器类

项目启动类


背景

分布式项目的单点登录分为认证服务(单点登录服务端)和业务服务(单点登录客户端)两个角色,

当访问业务服务时,认证服务客户端SDK校验一下是否有登录token,如果没有登录token,需要携带当前请求链接重定向到认证服务,认证通过后由认证服务重定向业务服务链接,实现单点登录。

gateway实现单点登录客户端功能,一般如果前后端项目是分离的,如果请求中没有携带登录token,直接返回需要认证,前后端没有分离的项目,可以做页面重定向操作。

本文主要讨论gateway的实现,认证服务需要自行实现。

实现

gateway

注册中心、配置中心用的nacos

nacos官网:home

配置可以参考:Springcloud+Druid+Mybatis+Seata+Nacos动态切换多数据源,分布式事务的实现_殷长庆的博客-CSDN博客_seata多数据源切换

maven配置

主要集成nacos注册、配置中心和gateway网关


        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- 转发ws协议请求 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

yml配置

server:
  port: 8888
spring:
  profiles:
    active: dev
  application:
    name: luckgateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: luck-cloud
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        namespace: luck-cloud
    gateway:
      routes:
        - id: lucksso
          uri: lb://lucksso
          predicates:
            - Path=/lucksso/**
        - id: luckbiz
          uri: lb://luckbiz
          predicates:
            - Path=/luckbiz/**
        - id: luckim
          uri: lb:ws://luckim
          predicates:
            - Path=/luckim/**
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,默认false,true时网关转发的微服务链接不带path前缀
          lower-case-service-id: true #使用小写服务名,默认是大写

secure:
  ignore:
    urls: #配置白名单路径
      - "/actuator/**"
      - "/lucksso/**"
      - "/resources/**"
  page:
    urls: #配置需要登录的页面路径
      - "/**"

页面登录拦截配置类

package com.luck.config;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 页面登录拦截配置
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Component
@ConfigurationProperties(prefix = "secure.page")
public class PageUrlsConfig {
	private List<String> urls;
}

白名单配置

package com.luck.config;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 白名单配置
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Component
@ConfigurationProperties(prefix = "secure.ignore")
public class IgnoreUrlsConfig {
	private List<String> urls;
}

token工具类

token的获取和校验工具

package com.luck.config;

import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.server.ServerWebExchange;

/**
 * 登录token工具类
 */
public class LoginTokenUtil {
	/** 登录服务地址 */
	public static final String LOGIN_PATH = "/lucksso/login?servers=";
	/** 登录成功后的回调接口地址 */
	public static final String LOGIN_CALLBACK_PATH = "/sso/login";
	/** 登录成功后的回调接口的token参数名称 */
	public static final String LOGIN_CALLBACK_TOKEN = "token";
	/** 登录成功后的回调接口的回调地址参数名称 */
	public static final String LOGIN_CALLBACK_URL = "url";

	/** 登录成功后的token名称 */
	public static final String LOGIN_TOKEN_NAME = "luckToken";

	/**
	 * 获取登录token
	 * @param exchange 上下文
	 * @return
	 */
	public static String getLoginToken(ServerWebExchange exchange) {

		if (null == exchange) {
			return null;
		}

		ServerHttpRequest request = exchange.getRequest();

		String loginToken = request.getHeaders().getFirst(LOGIN_TOKEN_NAME);

		if (StringUtils.isBlank(loginToken)) {
			Object token = exchange.getAttribute(LOGIN_TOKEN_NAME);
			if (null != token) {
				loginToken = (String) token;
			}
		}

		if (StringUtils.isBlank(loginToken)) {
			loginToken = request.getQueryParams().getFirst(LOGIN_TOKEN_NAME);
		}

		if (StringUtils.isBlank(loginToken)) {
			HttpCookie loginCookie = request.getCookies().getFirst(LOGIN_TOKEN_NAME);
			if (null != loginCookie) {
				loginToken = loginCookie.getValue();
			}
		}

		return loginToken;
	}

	/**
	 * 校验登录token是否有效
	 * @param loginToken 登录token
	 * @return
	 */
	public static boolean validateLoginToken(String loginToken) {
		if (StringUtils.isNoneBlank(loginToken)) {
			// do something
			return true;
		}
		return false;
	}

}

登录配置类

实现单点登录客户端核心逻辑,根据请求判断有没有登录token,可以从请求头获取或者cookie、链接参数获取,如果没有则重定向到认证服务,认证服务实现登录逻辑,回调网关接口,完成登录

package com.luck.config;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;

import reactor.core.publisher.Mono;

@Configuration
public class LoginConfig {
	/** 路由匹配器 */
	public static PathMatcher pathMatcher = new AntPathMatcher();
	/** 白名单 */
	@Autowired
	private IgnoreUrlsConfig ignoreUrlsConfig;
	/** 拦截名单 */
	@Autowired
	private PageUrlsConfig pageUrlsConfig;

	@FunctionalInterface
	public interface LoginFunction {
		public Mono<Void> run();
	}

	@Bean
	public WebFilter getSsoLoginFilter() {
		return new WebFilter() {

			@Override
			public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
				try {
					ServerHttpRequest request = exchange.getRequest();
					String path = request.getPath().toString();
					if (LoginTokenUtil.LOGIN_CALLBACK_PATH.equals(path)) {
						String loginToken = request.getQueryParams().getFirst(LoginTokenUtil.LOGIN_CALLBACK_TOKEN);
						String loginUrl = request.getQueryParams().getFirst(LoginTokenUtil.LOGIN_CALLBACK_URL);
						if (StringUtils.isAnyBlank(loginToken, loginUrl)) {
							throw new RuntimeException("参数错误!");
						}
						String url = URLDecoder.decode(loginUrl, "UTF-8");
						boolean validateLoginToken = LoginTokenUtil.validateLoginToken(loginToken);
						if (validateLoginToken) {
							ServerHttpResponse response = exchange.getResponse();
							response.setStatusCode(HttpStatus.SEE_OTHER);// 校验成功,重定向
							response.getHeaders().set(HttpHeaders.LOCATION, url);
							return exchange.getResponse().setComplete();
						}
						// 校验失败,抛异常或者重新执行登录
						// redirectSSO(exchange, request, url);
						throw new RuntimeException("token校验失败!");
					}
					String loginToken = LoginTokenUtil.getLoginToken(exchange);
					Mono<Void> result = null;
					if (StringUtils.isNoneBlank(loginToken)) {
						boolean hasLogin = LoginTokenUtil.validateLoginToken(loginToken);
						if (!hasLogin) {
							result = redirectSSO(exchange, request, path);
						} else {
							return chain.filter(exchange);
						}
					} else {
						result = redirectSSO(exchange, request, path);
					}
					if (null != result) {
						return result;
					}
					throw new RuntimeException("token校验失败!");
				} catch (Exception e) {
					exchange.getResponse().getHeaders().set("Content-Type", "application/json; charset=utf-8");
					return exchange.getResponse().writeWith(Mono.just(exchange.getResponse().bufferFactory().wrap(e.getMessage().getBytes())));
				}
			}

			/**
			 * 调单点登录
			 * @param exchange 上下文
			 * @param request 请求
			 * @param path 请求地址
			 * @return
			 */
			private Mono<Void> redirectSSO(ServerWebExchange exchange, ServerHttpRequest request, String path) {
				Mono<Void> result = match(pageUrlsConfig.getUrls(), ignoreUrlsConfig.getUrls(), path, () -> {
					if (isPage(path)) {
						URI uri = request.getURI();
						String url = "/";
						try {
							url = LoginTokenUtil.LOGIN_PATH // 登录服务地址
									+ URLEncoder.encode(uri.getScheme() + "://" + uri.getAuthority() // gateway服务(http://gateway)
											+ LoginTokenUtil.LOGIN_CALLBACK_PATH + "?" + LoginTokenUtil.LOGIN_CALLBACK_URL + "=" // gateway回调地址参数 http://gateway/sso/login?url=
											+ URLEncoder.encode(uri.toString(), "UTF-8"), "UTF-8");// 登录成功重定向地址
						} catch (UnsupportedEncodingException e1) {
							e1.printStackTrace();
						}
						ServerHttpResponse response = exchange.getResponse();
						response.setStatusCode(HttpStatus.SEE_OTHER);
						response.getHeaders().set(HttpHeaders.LOCATION, url);
						exchange.getResponse().getHeaders().set("Content-Type", "text/plain; charset=utf-8");
						return exchange.getResponse().setComplete();
					}
					return null;
				});
				return result;
			}

		};
	}

	/**
	 * 判断请求是不是页面请求
	 * @param path 请求路径
	 * @return
	 */
	private boolean isPage(String path) {
		return true;
	}

	/**
	 * 路由匹配 (并指定排除匹配符),如果匹配成功则执行认证函数 
	 * @param patterns 路由匹配符集合
	 * @param excludePatterns 要排除的路由匹配符集合
	 * @param path 请求链接
	 * @param function 要执行的方法 
	 */
	public Mono<Void> match(List<String> patterns, List<String> excludePatterns, String path, LoginFunction function) {
		if (isMatchCurrURI(patterns, path)) {
			if (isMatchCurrURI(excludePatterns, path) == false) {
				return function.run();
			}
		}
		return null;
	}

	/**
	 * 路由匹配 (使用当前URI) 
	 * @param patterns 路由匹配符集合 
	 * @param path 被匹配的路由  
	 * @return 是否匹配成功 
	 */
	public boolean isMatchCurrURI(List<String> patterns, String path) {
		return isMatch(patterns, path);
	}

	/**
	 * 路由匹配   
	 * @param patterns 路由匹配符集合 
	 * @param path 被匹配的路由  
	 * @return 是否匹配成功 
	 */
	public boolean isMatch(List<String> patterns, String path) {
		for (String pattern : patterns) {
			if (isMatch(pattern, path)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 路由匹配
	 * @param pattern 路由匹配符 
	 * @param path 被匹配的路由  
	 * @return 是否匹配成功 
	 */
	public boolean isMatch(String pattern, String path) {
		return pathMatcher.match(pattern, path);
	}
}

全局过滤器类

主要是为网关转发请求时添加登录token文章来源地址https://www.toymoban.com/news/detail-613407.html

package com.luck.config;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

/**
 * 全局过滤器,添加登录token请求头
 */
@Component
public class ForwardAuthFilter implements GlobalFilter {
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		ServerHttpRequest request = exchange//
				.getRequest()//
				.mutate()//
				.header(LoginTokenUtil.LOGIN_TOKEN_NAME, LoginTokenUtil.getLoginToken(exchange))//
				.build();//
		ServerWebExchange newExchange = exchange.mutate().request(request).build();
		return chain.filter(newExchange);
	}
}

项目启动类

package com.luck;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {

	public static void main(String[] args) {
		SpringApplication.run(GatewayApplication.class, args);
	}

}

到了这里,关于Springcloud gateway网关+认证服务+token方式,入口层认证统一微服务鉴权【设计实践】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Authorization Server入门 (十六) Spring Cloud Gateway对接认证服务

            之前虽然单独讲过Security Client和Resource Server的对接,但是都是基于Spring webmvc的,Gateway这种非阻塞式的网关是基于webflux的,对于集成Security相关内容略有不同,且涉及到代理其它微服务,所以会稍微比较麻烦些,今天就带大家来实现Gateway网关对接OAuth2认证服务。

    2024年02月10日
    浏览(64)
  • 【业务功能篇94】微服务-springcloud-springboot-认证服务-注册功能-第三方短信验证API

      结合我们前面介绍的商城的架构我们需要单独的搭建一个认证服务。   首先创建一个SpringBoot项目,然后添加对应的依赖   我们需要把认证服务注册到Nacos中,添加对应的依赖,然后完成对应的配置 放开Nacos注册中心 然后启动测试   然后我们整理登录和注册的相关

    2024年02月09日
    浏览(52)
  • 【业务功能篇96】微服务-springcloud-springboot-认证服务-登录注册功能-Auth2.0-分布式session

      通过最基础的登录操作来完成登录处理 登录页面处理 认证服务的处理 会员中心的认证逻辑 service中的具体认证处理   OAuth2.0是OAuth协议的延续版本,但不向前兼容OAuth 1.0(即完全废止了OAuth1.0)。 OAuth 2.0关注客户端开发者的简易性。要么通过组织在资源拥有者和HTTP服务商

    2024年02月09日
    浏览(54)
  • Spring实现HTTPS方式访问服务(单向认证)

    目录 前言 一、基本概念 二、创建keyStore、trustStore 三、服务端配置 四、客户端配置 总结 参考链接 本文通过Spring来配置https服务,后续会将相关的知识内容简单讲解一下,该文章主要是https单向认证,双向认证实现过程将在后续不久的另外一边文章涉及,感兴趣的小伙伴可以

    2024年02月16日
    浏览(40)
  • 【SpringCloud】Gateway服务网关

    Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 Gateway网关是我们服务的守门神,所有微服务的统一入口。 网关的

    2024年02月13日
    浏览(50)
  • SpringCloud - GateWay服务网关

    gateway 官网:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/ 在微服务架构中,一个系统往往由多个微服务组成,而这些服务可能部署在不同机房、不同地区、不同域名下。这种情况下,客户端(例如浏览器、手机、软件工具等)想要直接请求这些服务

    2024年02月07日
    浏览(49)
  • SpringCloud:Gateway服务网关

    网关(Gateway)是将两个使用不同协议的网络段连接在一起的设备。 网关的作用就是对两个网络段中的使用不同传输协议的数据进行互相的翻译转换。 创建服务 导入依赖 编写启动类 添加配置 Route Predicate Factories :: Spring Cloud Gateway 对所有路径都生效 全局过滤器的作用也是处理

    2024年02月01日
    浏览(54)
  • 微服务网关 —— SpringCloud Gateway

    Spring Cloud Gateway 基于 Spring 5、Spring Boot 2 和 Project Reactor 等技术,是在 Spring 生态系统之上构建的 API 网关服务,Gateway 旨在提供一种简单而有效的方式来对 API 进行路由以及提供一些强大的过滤器功能,例如熔断、限流、重试等 Spring Cloud Gateway 具有如下特性: 基于 Spring Frame

    2024年02月10日
    浏览(47)
  • 【SpringCloud技术专题】「Gateway网关系列」(2)微服务网关服务的Gateway功能配置指南分析

    Spring Cloud Gateway简介 Spring Cloud Gateway是Spring Cloud体系的第二代网关组件,基于Spring 5.0的新特性WebFlux进行开发,底层网络通信框架使用的是Netty,所以其吞吐量高、性能强劲,未来将会取代第一代的网关组件Zuul。 Spring Cloud Gateway可以通过服务发现组件自动转发请求,默认集成了

    2024年02月11日
    浏览(35)
  • SpringCloud_Gateway服务网关

    Spring Cloud Gateway 用\\\"Netty + Webflux\\\"实现,不需要导入Web依赖。 Webflux 模式替换了旧的Servlet线程模型。用少量的线程处理request和response io操作,这些线程称为Loop线程,而业务交给响应式编程框架处理,响应式编程是非常灵活的,用户可以将业务中阻塞的操作提交到响应式框架的

    2024年02月02日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包