SpringCloud项目,使用JWT+Gateway方案,实现单点登录

什么是单点登录?

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

为什么要使用单点登录?

原来登录的过程:
1)用户输入账号密码
2)提交到后台验证,成功后将用户存在Session
3)需要进行登录状态判断时,判断Session中是否存在该对象

分布式系统的多个相关的应用系统,都需要分别进行登录,非常繁琐。

存在的问题:

分布式系统有N个服务器,每个服务器有自己的Session,无法登录一次,所有服务器能判断用户登录状态

单点登录的实现方案

SSO有哪些常见的解决方案
1)使用Redis实现Session共享

有状态的登录,需要在服务器中保存用户的数据;REST架构推荐使用无状态通信,不在服务器端保存用户状态,服务器压力更小,成本更低,扩展更加容器。

2)使用Token机制实现
将用户的状态保存到客户端的cookie中,每次请求服务器时,都会携带用户信息,服务器对用户信息进行解析和判断,来进行登录鉴权。

1)用户输入账号密码,通过网关,进入验证服务
2)验证服务进行登录验证
3)验证成功后,将用户信息保存到token字符串,将token写入cookie
4)cookie被保存到用户浏览器中
5)用户再访问微服务时,经过网关,网关对token进行解析
6)解析成功,允许访问其他微服务
7)解析失败,不允许访问
这种方式是无状态登录,服务器不保存用户状态,状态保存到客户端,信息存在安全性问题,需要加密。

JWT

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
官网:https://jwt.io

 

可以在官网测试保存信息到JWT中,可以看到JWT分为三个部分:

  1. header 头部,包含声明类型和加密算法
  2. payload 负载,就是有效数据,一般是用户信息
  3. signature 签名,数据的认证信息

JWT的交互流程

  1. 用户登录,发送账号密码
  2. 服务的认证,通过后根据secret生成token
  3. 将生成的token返回给浏览器
  4. 用户每次请求携带token
  5. 服务端利用公钥解读jwt签名,判断签名有效后,从Payload中获取用户信息
  6. 处理请求,返回响应结果

单点登录的实现

  1. JWT+Gateway方案

  2. OAuth2方案

  3. 共享session

实现步骤:

  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配置,登录成功的处理器

    
    @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
     
        }
     
    }
    
  8. 在网关配置用户的路由

         - id: user-service-route
              uri: lb://user-service
              predicates:
                - Path=/login,/logout,/user/**
  9. 开发放行接口的白名单

    user:
      white-list: # 自定义白名单
        - /login
        - /logout
    @Data
    @Configuration
    @ConfigurationProperties(prefix = "user")
    public class WhiteListConfig {
    
        private List<String> whiteList;
    }
  10. 编写网关过滤器

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

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SpringCloud是一个基于Spring Boot的开源微服务框架。SpringCloud GatewaySpringCloud生态中的一个组件,提供了一种基于路由的API网关解决方案JWT是JSON WEB Token的缩写,是一种用于身份认证和授权的开放标准。OAuth2是一种授权框架,用于向用户授权第三方应用访问他们的资源。 在微服务架构中,每个服务都是独立的,网关作为服务的入口,可以实现对外的请求过滤和路由。SpringCloud Gateway使用HttpClient进行内部请求的调度和路由。同时,它还提供了一些高阶的路由和过滤功能,如重定向、URL重写、限流、熔断、重试等。 JWT是一种轻量级的认证方案,通过在HTTP请求中添加一个JSON WEB Token,实现对用户进行身份认证和授权。JWT使用极大地简化了认证过程,前后端可以通过JWT判断用户的身份和权限。 OAuth2为开发者提供了一种授权框架,可以授权第三方应用获取用户访问他们的资源。OAuth2支持多种授权类型,如授权码模式、密码模式、客户端模式和隐式模式使用OAuth2,可以更好地保护用户的隐私和安全。 综上所述,SpringCloud GatewayJWT和OAuth2都是现代化的解决方案,对于设计和开发微服务架构的应用程序来说,它们都是必不可少的组件。有了它们,开发人员可以更好的搭建分布式架构,确保数据安全性、隐私安全性和服务的可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值