乐购商城微服务项目实战之网关与OAuth2授权详解

微服务网关和JWT令牌

不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求,如果让客户端直接与各个微服务通信,会有以下的问题:

  1. 客户端会多次请求不同的微服务,增加了客户端的复杂性
  2. 存在跨域请求,在一定场景下处理相对复杂
  3. 认证复杂,每个服务都需要独立认证
  4. 难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个,如果客户端直接与微服务通信,那么重构将会很难实施
  5. 某些微服务可能使用了防火墙/浏览器不友好的协议,直接访问会有一定的困难

微服务网关的主要作用

  1. 整合微服务功能,形成一套系统
  2. 在微服务架构中实现日志的统一记录管理
  3. 实现用户的操作跟踪
  4. 实现限流操作
  5. 用户权限认证操作

在这里插入图片描述
在这里插入图片描述

实现微服务网关的技术

  1. Nginx:是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务
  2. Zuul:是Netflix出品的一个基于JVM路由和服务端的负载均衡器
  3. spring-cloud-gateway:是spring出品的基于spring的网关项目,集成断路器,路径重写,性能比Zuul好

真实架构

在这里插入图片描述

跨域问题基本的三种解决方案

  1. 注解@CrossOrgin

  2. 写过滤器

  3. 网关跨域

    spring:
    cloud:
    gateway:
    globalcors:
    cors-configurations:
    ‘[/**]’
    allow-credentials: true 允许携带令牌信息
    allowed-origins: “"
    allowed-headers: "

    allowed-methods: “*”
    max-age: 3600
    在这里插入图片描述在这里插入图片描述

网关可以进行限流,当我们的系统被频繁地请求的时候,就有可能将系统压垮,所以为了解决这个问题,需要在每一个微服务中做限流操作,但是如果有了网关,就可以解决这个问题。

令牌桶算法

  1. 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理
  2. 根据限流大小,设置按照一定的速率往桶里添加令牌
  3. 设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝
  4. 请求到达后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他业务逻辑,处理完业务逻辑之后,将令牌直接删除
  5. 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后不会删除令牌,以此保证足够的限流在这里插入图片描述

漏桶算法

  1. 水(请求)从上方倒入水桶,从水桶下方流出(被处理)
  2. 来不及流出的水存在水桶中(缓冲),以固定速率流出
  3. 水桶满后水溢出(丢弃)
  4. 这个算法的核心是:缓存请求、匀速处理、多余的请求直接丢弃在这里插入图片描述

两种算法的区别:两者主要区别在于漏桶算法能够强行限制数据的传输速率,而令牌桶算法在能够限制数据的平均传输速率外,还允许某种程度的突发传输。在令牌桶算法中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到到达用户配置的门限,所以它适合于具有突发性的流量

spring cloud gateway默认使用Redis的RateLimter限流算法来实现,所以我们要使用首先需要引入Redis的依赖

//根据IP限流
@SpringBootApplication
public class GatewayApplication{
    public static void main(String[] args){
        SpringApplication.run(GatewayApplication.class, args);
    }
    @Bean(name = "ipKeyResolver")
    public KeyResolver userKeyResolver(){
        return new KeyResolver(){
            @Override
            public Mono<String> resolve(ServerWebExchange exchange){
                //获取客户端IP
                String hostName = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
                sout("hostName: " + hostName);
                return Mono.just(hostName);
            }
        };
    }
}

在这里插入图片描述

RBAC权限数据管理

RBAC(基于角色的权限控制 role base access control)是一种设计模式,是用来设计和管理权限相关数据的一种模型,一般要求用户和角色、角色和权限是多对多的关联。具体表结构:在这里插入图片描述

// 保存角色时先删除角色用户的关联表,再逐一添加用户的关联
//controller层
@ApiOperation(value="保存", notes="ID存在修改,不存在添加")
@PostMapping("/save")
public ResponseBean save(T entity) throws Exception {
    ResponseBean rm = new ResponseBean();
    try{
        beforeSave(entity);//保存前处理实体类
        service.saveOrUpdate(entity);
        rm.setModel(entity);
    }catch(Exception e){
        e.printStackTrace();
        rm.setSuccess(false);
        rm.setMsg("保存失败");
    }
    return rm;
}

//service实现类重写
@Override
@Transactional(readOnly = false)
public boolean saveOrUpdate(Role entity){
    RoleDao dao = ((RoleDao) getBaseMapper());
    boolean result = super.saveOrUpdate(entity);
    
    dao.deleteUserByRole(entity.getId());//删除角色的用户
    //添加角色和权限关系
    Role role (Role) entity;
    //添加用户和角色关系
    if(null != role.getUserIds()){
        for(Long userId : role.getUserIds()){
            dao.insertUserAndRole(userId, entity.getId());
        }
    }
    return result;
}

单点登录(SSO Sigle Sign On):在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。

普通认证机制

我们在浏览器(Browser)中访问一个应用,这个应用需要登陆,我们填写完用户名和密码后,完成登录认证。这时我们在这个用户的session中标记登录状态为yes(已登录),同时在浏览器(Browser)中写入Cookie是这个用户的唯一标识。下次我们再访问这个应用的时候,请求会带上这个Cookie,服务端会根据这个Cookie找到对应的session,通过session来判断这个用户是否登录。如果不做特殊配置,这个Cookie的名字叫做jsessionid,值在服务端(server)是唯一的(类比去银行办业务)

在这里插入图片描述

基于token的认证:(类比电影院的票)

在这里插入图片描述

JWT讲解

在这里插入图片描述

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。一个JWT实际上就是一个字符串,它由三部分组成:头部(Header)、载荷(playload)与签名(signature)

  1. 头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等,这也可以被表示成一个JSON对象

{“type”:“jwt”,“alg”:“HS256”}

  1. 载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分

    1. 标准中注册的时间(建议但不强制使用)

      iss: jwt签发者
      sub: jwt所面向的用户
      aud: 接收jwt的一方
      exp: jwt的过期时间,这个过期时间必须要大于签发时间
      nbf: 定义在什么时间之前,该jwt都是不可用的
      iat: jwt的签发时间
      jti: jwt的唯一身份标识,主要用来作为一次性token,从而避免重放攻击

    2. 公共的声明:公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密

    3. 私有的声明:私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base是对称解密的,意味着该部分信息可以归类为明文信息。

  2. 签名:jwt的第三部分是一个签证信息,这个签证信息由三部分组成:header(base64后的)、payload(base64后的)、secret。这个部分需要base64加密后的header和base64加密后的payload使用,连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。

    注意:secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,他就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret,那就意味着客户端是可以自我签发jwt的了

鉴权处理

在这里插入图片描述

Oauth2模块

我们颁发的令牌,会携带用户信息,包括角色,这些用户信息,需要使用RBAC微服务查询获得,然后通过JWT令牌载荷存储,所以这里我们按照spring security规范实现自定义的UserDetailService,获取用户角色权限RBAC数据,存储到令牌

使用自定义的UserDetailService访问RBAC

/**
 * @author William
 * @create 2022-05-12 20:16
 */
@FeignClient(name = "security-service", fallback = UserClient.UseerClientFallback.class)
public interface UserClient extends UserApi {

    @Component
    @RequestMapping("/fallback")//这个可以避免容器中requestMapping重复
    class UseerClientFallback implements UserClient {

        private static final Logger LOGGER = LoggerFactory.getLogger(UseerClientFallback.class);

        @Override
        public User getByUserName(String userName) {
            LOGGER.info("异常发生,进入fallback方法");
            return null;
        }

        @Override
        public List<Role> selectRolesByUserId(Long id) {
            LOGGER.info("异常发生,进入fallback方法");
            return null;
        }
    }
}
/**
 * 自定义UserDetailService
 * @author William
 * @create 2022-05-12 20:30
 */
@Service
public class UserDetailServiceImpl implements UserDetailsService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UserDetailServiceImpl.class);

    @Autowired
    private UserClient userClient;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //通过feign调用用户微服务
        User user = userClient.getByUserName(username);
        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        if(user != null){
            LOGGER.debug("current user = " + user);
            //获取用户的权限(角色)
            List<Role> roles = userClient.selectRolesByUserId(user.getId());
            for(Role role : roles){
                if(role != null && role.getName() != null){
                    //spring security要求权限名称ROLE_ADMIN ROLE_MANAGER,有前缀"ROLE_
                    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_" + role.getName());
                    grantedAuthorities.add(grantedAuthority);
                }
            }

        }
        return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), grantedAuthorities);
    }
}

Oauth2-授权服务器配置(代码看PPT)

非对称加密

非对称加密算法需要两个密钥:公钥和私钥。公钥与私钥是一对,如果用公钥对数据进行加密,只有用对应的私钥才能解密。因为加密和解密使用的是两个不同的密钥,所以叫作非对称加密算法。非等称加密算法实现机密信息交换的基本过程是:

  1. 甲方生成一对密钥并将公钥公开,需要向甲方发送信息的其他角色(乙方)使用该密钥(甲方的公钥)对机密信息进行加密后再发送给甲方
  2. 甲方再用自己私钥对加密后的信息进行解密
  3. 甲方想要回复乙方时正好相反,使用乙方的公钥对数据进行加密,同理,乙方使用自己的私钥来进行解密
    在这里插入图片描述

上图业务流程如下:(重要)

  1. 客户端请求认证服务申请令牌
  2. 认证服务生成令牌:认证服务采用非对称加密算法,使用密钥生成令牌
  3. 客户端携带令牌访问资源 服务客户端在Http header中添加:Authorization Bearer令牌
  4. 资源服务请求认证服务校验令牌的有效性 服务接收到令牌,使用公钥校验令牌的合法性
  5. 令牌有效,资源服务向客户端响应资源信息

生成公钥私钥:我们采用JWT私钥颁发令牌,公钥校验令牌,这里先使用keytool工具生成公钥私钥证书

  1. 生成密钥证书 下边命令生成密钥证书,采用RSA算法每个证书包含公钥和私钥
    创建一个文件夹,在该文件夹下执行如下命令行:

    keytool -genkeypair -alias kaikeba -keyalg RSA -keypass kaikeba -keystore kaikeka.jks - storepass kaikeba

keytool是一个java提供的证书管理工具

-alias:密钥的别名
-keyalg: 使用的hash算法
-keypass: 密钥的访问密码
-keystore: 密钥库文件名, xc.keystore保存生成了的证书
-storepass: 密钥库的访问资料

  1. 查询证书信息

    keytool -list -keystore kaikeba.jks

  2. 删除别名

    keytool -delete -alias kaikeba -keystore kaikeba.jks

导出公钥

openssl是一个加解密工具包,这里使用openssl来导出公钥信息

再去测试私钥生成令牌,公钥校验令牌

**资源服务器配置:**基本上所有微服务都是资源服务,授权服务器采用私钥加密生成令牌,对外向资源服务器提供公钥,资源服务器使用公钥来校验令牌的合法性。让公钥拷贝到public.key文件中,将此文件拷贝到每一个需要的资源服务工程的classpath下,例如RBAC微服务

Oauth2登录实现

在这里插入图片描述

登录流程:(重要)

  1. 前端提交用户名、密码
  2. 用户微服务得到用户名密码
  3. 用户微服务组织数据包括:client_id;client_secret组织Basic Authorization,用户名,密码等参数
  4. 用户微服务使用restTemplate发送HTTP请求给授权中心微服务
  5. 授权中心微服务校验通过颁发令牌
  6. 前端将令牌存储到sessionStorage中,下次访问资源服务器通过Header携带访问
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值