定制设计SpringCloud学习之Gateway—单点登录的实现

文章目录

1.Gateway的介绍

路由(Route): 定制设计路由是网关的基本组成部分,定制设计路由信息由ID、目标URL、定制设计一组断言和一组过滤器组成,定制设计如果断言为真,定制设计则说明请求的URL定制设计和配置匹配。
断言(Predicate): Java8定制设计中的断言函数,Spring Cloud Gateway定制设计中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway定制设计中的断言函数允许开发定制设计者自定义匹配来自于Http Request定制设计中的任何信息,定制设计比如请求头和参数等
过滤器(Flute): 定制设计一个标准的SpringWebFilter,Spring Cloud Gateway中的Filter定制设计分为两种类型,分别是Gateway Filter和Globa lFilter,过滤器将会对请求和响应进行处理。

2.Gateway的作用

Gateway是SpringCloud提供的API网关,提供主要功能有:路由和鉴权以及限流、统一配置等。

3.Gateway的工作原理

1.客户端发送请求到网关2.网关通过HandlerMapping处理器映射器获得Handler处理器3.Handler执行通过网关内部的过滤器链,过滤器分为两种:pre前置、post后置4.前置过滤器主要实现鉴权和路由,后置过滤器可以进行性能监控和数据统计等5.通过所有过滤器才能访问到需要的服务
  • 1
  • 2
  • 3
  • 4
  • 5

4.Gateway的路由功能

使用过程:
1.创建网关项目
2.引入geteway依赖
3.网关需要注册到注册中心
5.配置路由

spring	cloud:		gateway:			routes:	#路由			- id: 路由id			  uri: lb: //order-service-ruter	#服务名			  predicates:	#断言			  - Path=/order/**,/orders/**			- id: 路由id			  uri: lb: //product-service-ruter	#服务名			  predicates:	#断言			  - Path=/product/**,/products/**			  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

5.Gateway跨域配置

通过网关统一跨域,其它服务中不配置跨域

spring:  application:    name: gateway-service  cloud:    gateway:      globalcors:        cors-configurations: # 跨域配置          '[/**]': # 匹配所有路径            allowed-origins: # 允许的域名              - "http://localhost:8080"            allowed-headers: "*" # 允许的请求头            allowed-methods: "*" # 允许的方法            allow-credentials: true # 是否携带cookie
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

6.Gateway过滤器

从过滤器生命周期(影响时机点)的角度来说,主要有两个pre和post:

生命周期时机点作用
pre这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
post这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

从过滤器类型的角度,Spring Cloud GateWay的过滤器分为GateWayFilter和GlobalFilter两种

过滤器类型影响范围
GateWayFilter应用到单个路由上
GlobalFilter应用到所有的路由上

7.使用Gateway实现

单点登录(Single Sign On),简称为 SSO,在分布式架构项目中,只需要在一个节点进行登录验证,就能够在其它的所有相关节点实现访问。

实现方案:

  1. JWT+Gateway方案
  2. OAuth2方案
  3. 共享session

以下是使用JWT+Gateway方案实现单点登录


实现步骤:

1.创建数据库,数据表(商品,订单,用户)

2.创建用户服务,添加所需依赖

<dependencies>    <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>    <dependency>        <groupId>com.baomidou</groupId>        <artifactId>mybatis-plus-boot-starter</artifactId>        <version>3.5.1</version>    </dependency>    <dependency>        <groupId>mysql</groupId>        <artifactId>mysql-connector-java</artifactId>        <scope>runtime</scope>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-test</artifactId>        <scope>test</scope>    </dependency>    <dependency>        <groupId>org.springframework.security</groupId>        <artifactId>spring-security-test</artifactId>        <scope>test</scope>    </dependency>    <dependency>        <groupId>com.blb</groupId>        <artifactId>comment_api</artifactId>        <version>1.0-SNAPSHOT</version>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-web</artifactId>    </dependency></dependencies>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

3.注册到Eureka上(启动类加注解@EnableDiscoveryClient、写配置文件)

server.port=10000#服务名spring.application.name=mysql-user-service#是否从注册中心获得数据eureka.client.register-with-eureka=true#是否注册到注册中心上eureka.client.fetch-registry=true#配置注册中心的地址eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8888/eureka/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4.配置数据源和mybatis-plus

#数据库配置spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/feigin_user?serverTimezone=UTCspring.datasource.username=rootspring.datasource.password=rootmybatis-plus.type-aliases-package=com.blb.comment.entitymybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImplmybatis-plus.configuration.map-underscore-to-camel-case=truemybatis-plus.mapper-locations=classpath:mapper/*.xml
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

5.编写entity、mapper、service

略…

6.编写UserDetailsService实现类

@Servicepublic class UserDetailsServiceImpl implements UserDetailsService {    @Autowired    private TUserMapper userMapper;    @Override    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {        //按用户名查询        TUser user = userMapper.selectOne(new QueryWrapper<TUser>().lambda().eq(TUser::getUsername, s));        if(user == null){            throw new UsernameNotFoundException("用户名不存在");        }        //返回正确的用户信息        return new org.springframework.security.core.userdetails.User(s,user.getPassword(),                AuthorityUtils.commaSeparatedStringToAuthorityList(""));    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

7.编写Security配置,登录成功的处理器

@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private UserDetailsService userDetailsService;    @Autowired    private LoginSuccessHandler loginSuccessHandler;    @Bean    public BCryptPasswordEncoder bCryptPasswordEncoder(){        return new BCryptPasswordEncoder();    }    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        //配置自定义登录逻辑        auth.userDetailsService(userDetailsService);    }    @Override    protected void configure(HttpSecurity http) throws Exception {        //配置放行url        http.authorizeRequests()                .antMatchers("/swagger-ui.html","/swagger-resources/**","/webjars/**","/*/api-docs"                        ,"/login","/logout").permitAll()                .anyRequest().authenticated()               //配置其它url要验证                .and()                .formLogin()                                //配置登录相关                .successHandler(loginSuccessHandler)  //配置登录成功的处理器                .failureHandler((req,resp,auth) -> {        //配置登录失败的处理器                    ResponseResult.write(resp, ResponseResult.error(ResponseStatus.LOGIN_ERROR));                })                .and()                .exceptionHandling()                .authenticationEntryPoint((req,resp,auth) ->{ //配置拦截未登录请求的处理                    ResponseResult.write(resp,ResponseResult.error(ResponseStatus.AUTHENTICATE_ERROR));                })                .and()                .logout()                .logoutSuccessHandler((req,resp,auth) ->{     //配置登出处理器                    ResponseResult.write(resp,ResponseResult.ok("注销成功"));                })                .clearAuthentication(true)                     //清除验证缓存                .and()                .csrf()                .disable()                                    //关闭csrf保护                .sessionManagement()                .sessionCreationPolicy(SessionCreationPolicy.STATELESS); //不使用session    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
@Slf4j@Componentpublic class LoginSuccessHandler implements AuthenticationSuccessHandler {    @Override    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {        //获得用户名        User user = (User) authentication.getPrincipal();        //将用户名生成jwt token        String token = JwtUtil.generateToken(user.getUsername(), RsaUtil.privateKey, JwtUtil.EXPIRE_MINUTES);        //将token 发送给前端        UserTokenVO userTokenVo = new UserTokenVO(user.getUsername(),token);        ResponseResult.write(response,ResponseResult.ok(userTokenVo));        log.info("user:{}  token:{}",user.getUsername() , token);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

8.在网关配置用户的路由

server:  port: 9999eureka:  client:    fetch-registry: true    register-with-eureka: true    serviceUrl:      defaultZone: http://127.0.0.1:8888/eureka/# 网关配置spring:  application:    name: mysql-gateway-server  cloud:    gateway:      routes:      - id: order-server-route	#订单路由        uri: lb://mysql-order-server  #订单服务名称        predicates: #断言        - Path=/orderlist/**,/order/**  #匹配路径      - id: product-server-route	#商品路由        uri: lb://mysql-product-server  #商品服务名称        predicates: #断言        - Path=/productlist/**,/product/**  #匹配路径      - id: user-server-route	#用户路由        uri: lb://mysql-user-service  #用户服务名称        predicates: #断言        - Path=/login,/logout,/user/**  #匹配路径      globalcors:        cors-configurations: # 跨域配置          '[/**]': # 匹配所有路径            allowed-origins: # 允许的域名(前端启动端口)              - "http://localhost:8080"            allowed-headers: "*" # 允许的请求头            allowed-methods: "*" # 允许的方法            allow-credentials: true # 是否携带cookieuser:  white-list: # 白名单    - /login    - /logout
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

9.在gateway中编写WhiteListConfig(开发放行接口的白名单)

@Data@Configuration@ConfigurationProperties(prefix = "user")public class WhiteListConfig {    //放行白名单    private List<String> whiteList;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

10.编写网关过滤器(验证用户token)

/** * 用户验证过滤器 * @author Charon */@Slf4j@Componentpublic class AuthenticationFilter implements GlobalFilter, Ordered {    @Autowired    private WhiteListConfig whiteListConfig;    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {        //获得请求和响应对象        ServerHttpRequest request = exchange.getRequest();        ServerHttpResponse response = exchange.getResponse();        //对白名单中的地址放行        List<String> whiteList = whiteListConfig.getWhiteList();        for(String str : whiteList){            if(request.getURI().getPath().contains(str)){                log.info("白名单,放行{}",request.getURI().getPath());                return chain.filter(exchange);            }        }        //获得请求头中Authorization token信息        String token = request.getHeaders().getFirst("Authorization");        try{            //解析token            String username = JwtUtil.getUsernameFromToken(token, RsaUtil.publicKey);            log.info("{}解析成功,放行{}",username,request.getURI().getPath());            return chain.filter(exchange);        }catch (Exception ex){            log.error("token解析失败",ex);            //返回验证失败的响应信息            response.setStatusCode(HttpStatus.UNAUTHORIZED);            DataBuffer wrap = response.bufferFactory().wrap("验证错误,需要登录".getBytes());            return response.writeWith(Mono.just(wrap));        }    }    @Override    public int getOrder() {        return 0;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

使用springboot+eureka+feign+gateway+vue实现单点登录后对商品、订单进行CRUD,具体代码详见

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发