Spring-Cloud之Gateway的基础使用以及使用JWT+Gateway实现单点登录

Gateway的作用

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

 Gateway的工作原理

 

1.客户端发送请求到网关。

2.网关通过HandlerMapping处理器映射获得Handler处理器。

3.Handler执行通过网关内部的过滤器链,过滤器分为两种:pre前置、post后置。

4.前置过滤器主要实现鉴权和路由,后置过滤器可以进行性能监控和数据统计等。

5. 通过所有过滤器才能访问到需要的服务。

Gateway的路由功能

使用过程:

1、创建网关项目

2、 引入gateway依赖

3、 网关需要注册到注册中心

4、 配置路由

spring:
  cloud:
    gateway:
      routes: # 路由
      - id: order-service-route
        uri: lb://order-service
        predicates: # 断言
        - Path=/order/**,/orders/**
      - id: product-service-route
        uri: lb://product-service
        predicates: # 断言
        - Path=/product/**,/products/**

Gateway跨域配置

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

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      globalcors:
        cors-configurations: # 跨域配置
          '[/**]': # 匹配所有路径
            allowed-origins: # 允许的域名
              - "http://localhost:8080"
            allowed-headers: "*" # 允许的请求头
            allowed-methods: "*" # 允许的方法
            allow-credentials: true # 是否携带cookie

Gateway过滤器

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

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

 单点登录的实现

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

实现方案:

1、JWT+Gateway方案

2、OAuth2方案

3、共享session

JWT+Gateway方案

实现步骤:

1、创建用户数据库,用户表

2、创建用户服务,添加依赖

<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>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.1</version>
</dependency>

<dependency>
<groupId>com.blb</groupId>
<version>0.0.1-SNAPSHOT</version>
<artifactId>common_api</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

3、注册到Eureka上:在启动类上加注解

@EnableDiscoveryClient

4、配置数据源和mybatis-plus

5、编写entity、mapper、service

6、编写UserDetailsService实现类

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Resource
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //按用户名查询
        User user = userMapper.selectOne(new QueryWrapper<User>().lambda().eq(User::getUsername, s));
        if(user==null){
            throw new UsernameNotFoundException("用户名不存在");
        }
        //返回正确的用户信息
         return new org.springframework.security.core.userdetails.User(s,user.getPassword(),
                 AuthorityUtils.commaSeparatedStringToAuthorityList(""));
    }
}

7、编写Security配置类,登录成功的处理器生成token

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private LoginSuccessHandler loginSuccessHandler;


    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    /**
     * 用来记录账号,密码,角色信息。
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //配置自定义登录逻辑
        auth.userDetailsService(userDetailsService);
    }

    /**
     * 也就是对角色的权限——所能访问的路径做出限制
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置放行url
        http.authorizeRequests()
                .antMatchers("/login","/logout","/swagger-ui.html","/swagger-recources/**","/webjars/**","/api-docs").permitAll()
                .anyRequest().authenticated()               //配置其它url要验证
                .and()
                .formLogin()                                //配置登录相关
                .successHandler(new 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

    }

}

 登录成功的处理器

@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, 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);
    }
}

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

  - id: user-service-route
          uri: lb://user-service
          predicates:
            - Path=/login,/logout,/user/**

9、开发放行接口的白名单

user:
  white-list: # 自定义白名单
    - /login
    - /logout

10、编写一个配置类区扫描配置文件中的白名单

@Data
@Configuration
@ConfigurationProperties(prefix = "user")
public class WhiteListConfig {

    private List<String> whiteList;
}

11、编写一个网关的过滤器

/**
 * 用户验证过滤器
 */
@Slf4j
@Component
public 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;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值