我的编程空间,编程开发者的网络收藏夹
学习永远不晚

基于JWT规范实现的认证微服务

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

基于JWT规范实现的认证微服务

基于JWT规范实现的认证微服务本文由公众号EAWorld翻译发表,转载需注明出处。

作者:Marcelo Fonseca

译者:白小白 

原题:Building an authentication micro-service with JWT standard

原文:http://t.cn/EI67VmL

全文2326字,阅读约需要5分钟

目录:

一、微服务介绍

二、随之而来的认证和授权问题

三、项目架构通信

四、用于签名以及验证的公钥和私钥令牌

五、项目数据库同步问题

一、微服务介绍

微服务日渐流行,几乎所有流行语言都提供了两种框架实现,一是面向Web开发的大型框架,一是面向小型应用的微框架。轻量级框架作为微服务架构来说,是个好的选择。微服务架构有很多优势,诸如高可维护性,独立部署等等。微服务架构让我们可以针对特定语言选择最优的解决方案来建立特定的服务,比如,针对爬虫类应用或者AI场景,我们可以选择建立一个Python服务;针对加密库的场景建立JS服务;针对Active Record的场景建立Ruby服务等等。基于这样的理念,我们不需要受限于使用单一语言来建立整个后端服务

下面我列出了各种语言提供的微框架列表:

  • Python - Flask

  • Javascript - ExpressJS

  • Ruby - Sinatra

  • Go - Martini

  • Java - Spark

  • C# - nancy

  • C++ - Crow

  • PHP - silex

二、随之而来的认证和授权问题

在微服务架构下,前后端的认证逻辑相比常规的CS应用要复杂的多。客户端与后端的API服务器并不是一对一的关系,我们需要管理很多的后端服务,需要对更多的应用路由提供保护。为了解决这一问题,人们实践了很多方式来建立微服务架构下的认证和授权逻辑。本文展示了其中一种方案,基于JSON Web Tokens(JWT)标准来实现一个简单的认证和授权服务。

三、项目架构通信

简化起见,示例中只实现了两个后端服务。我将建立一个用于认证和授权的expressJS应用,以及一个Sinatra应用来作为博客服务的后端。目前为止,在本例 中将有两个后端以及一个前端。

下面介绍一下应用间通信的实现机制。

前后端通信机制

基于JWT规范实现的认证微服务

  1. ExpressJS实现了前端应用的用户注册和登陆。

  2. 如果认证成功,ExpressJS应用将返回一个JWT令牌。

  3. 前端将这一令牌附加在请求的消息头中用以访问Sinatra应用数据。

服务间通信机制

当我们需要实现后端之间的通信时,就需要利用这样的机制。作为示例场景,假设还有一个Flask API后端用于爬取网络上的内容,并更新Sinatra博客应用中的数据。这样我们就一共有了三个后端和一个前端。

基于JWT规范实现的认证微服务

  1. Flask应用向ExpressJS应用请求JWT令牌。

  2. 请求成功后,ExpressJS应用返回令牌。

  3. Flask应用将令牌附加在请求的消息头,并访问Sinatra应用的后端路由。

此处需要注意两件事。无论是用户发出请求或者后端发出请求,都需要合法的身份来进行认证以及访问其他后端。但作为后端服务来讲是不会使用邮件和密码的,而是以API秘钥作为身份的证明代之。比如,Flask应用向ExpressJS应用的路由发送一个登陆秘钥,只要秘钥是正确的,就可以授权Flask服务获得JWT令牌。

四、用于签名以及验证的

公钥和私钥令牌

在这套架构下,所有的微服务应用将使用其自身的JWT库来对访问请求进行认证并保护其API路由。此处我们将使用JWT RSA256策略。认证服务ExpressJS将同时持有私钥和公钥。使用私钥来对用户或应用的令牌进行签名,用公钥对令牌进行解码和验证。其他服务将仅持有公钥来进行验证。

使用RSA算法需要生成一个公钥/私钥对。可以通过如下的代码在终端中实现,作为执行结果,代码将生成.pem文件:

openssl genrsa -des3 -out private.pem 2048openssl rsa -in private.pem -outform PEM -pubout -out public.pem

(左右滑动查看全部代码)

签名令牌

在用户或者API的登陆路由中实现令牌签名。下面的代码示例了ExpressJS认证服务的用户登陆路由。只要用户身份是合法的,代码将访问私钥rsa2048priv.pem并且签名一个新的JWT令牌。

  1. // User sign-in route with JWT RSA algorithm example

  2. var User = require('../models/user')

  3. var express = require('express');

  4. var router = express.Router();

  5. const mongoose = require('mongoose');

  6. const bcrypt = require('bcrypt');

  7. const jwt = require('jsonwebtoken');

  8. const fs = require('fs');

  9. router.route('/sign-in').post(function(req, res, next){

  10.  User.find({ email: req.body.email}).then(user => {

  11.    if (user.length < 1)

  12.      return res.status(400).json({message: 'Authentication failed.'});

  13.    bcrypt.compare(req.body.password, user[0].passwordHash, (err, success) => {

  14.      if(success){

  15.        let cert = fs.readFileSync('../rsa_2048_priv.pem');

  16.        const token = jwt.sign(

  17.          {

  18.            email: user[0].email,

  19.            //id: user[0]._id,

  20.          },

  21.          cert,

  22.          {

  23.            expiresIn: '1h',

  24.            algorithm: 'RS256',

  25.            issuer: user[0].role,

  26.          }

  27.        );

  28.        res.status(200).json({token: token, message: 'Successfully authenticated.'});

  29.      }else

  30.        return res.status(400).json({message: 'Authentication failed.'});

  31.    });

  32.  });

  33. });

(左右滑动查看全部代码)

验证令牌

所有的服务都需要对持有合法JWT令牌的进站请求进行验证。这可以通过在应用中建立一个中间件来实现。这一中间件将访问公钥pem文件来对令牌进行解码和验证。在ExpressJS或者Sinatra服务中,这样的中间件代码类似如下所示。

ExpressJS认证和授权中间件代码:

  1. // JWT authentication middleware example.

  2. // Uses RS256 strategy with .pem key pair files.

  3. const fs = require('fs');

  4. const jwt = require('jsonwebtoken');

  5. module.exports = (req, res, next) => {

  6.    let publicKey = fs.readFileSync('../rsa_2048_pub.pem');

  7.    try{

  8.        const token = req.headers.authorization.split(' ')[1]; //req.headers.token;

  9.        console.log(token);

  10.        var decoded = jwt.verify(token, publicKey)

  11.        console.log(decoded);

  12.        next();

  13.    }catch(err){

  14.      return res.status(401).json({error: err, message: 'Invalid token.'});

  15.    }

  16. };

(左右滑动查看全部代码)

Sinatra认证和授权中间件代码:

  1. # To connect this middleware.rb file to your sinatra app

  2. # add 'use JWTAuthorization' as one of your first lines in

  3. # your Application class.

  4. # e.g.

  5. # require 'middlewares.rb'

  6. # class Application < Sinatra::Base

  7. #   use JWTAuthorization

  8. #   ...

  9. # end

  10. require 'sinatra/json'

  11. require 'jwt'

  12. class JWTAuthorization

  13.  def initialize app

  14.    @app = app

  15.  end

  16.  def call env

  17.    begin

  18.      # env.fetch gets http header

  19.      # bearer = env.fetch('HTTP_AUTHORIZATION', '').split(' ')[1]    # also work

  20.      bearer = env.fetch('HTTP_AUTHORIZATION').slice(7..-1)           # gets JWT token

  21.      key = OpenSSL::PKey::RSA.new File.read '../rsa_2048_pub.pem'    # read public key pem file

  22.      payload = JWT.decode bearer, key, true, { algorithm: 'RS256'}   # decode and verify token with pub key

  23.      claims = payload.first

  24.      # current_user is defined by env[:user].

  25.      # useful to define current_user if you are using pundit gem

  26.      if claims['iss'] == 'user'

  27.        env[:user] = User.find_by_email(claims['email'])

  28.      end

  29.      # access your claims here...

  30.      @app.call env

  31.    rescue JWT::DecodeError

  32.      [401, { 'Content-Type' => 'text/plain' }, ['A token must be passed.']]

  33.    rescue JWT::ExpiredSignature

  34.      [403, { 'Content-Type' => 'text/plain' }, ['The token has expired.']]

  35.    rescue JWT::InvalidIssuerError

  36.      [403, { 'Content-Type' => 'text/plain' }, ['The token does not have a valid issuer.']]

  37.    rescue JWT::InvalidIatError

  38.      [403, { 'Content-Type' => 'text/plain' }, ['The token does not have a valid "issued at" time.']]

  39.    # useful only if using pundit gem

  40.    rescue Pundit::NotAuthorizedError

  41.      [401, { 'Content-Type' => 'text/plain' }, ['Unauthorized access.']]

  42.    end

  43.  end

  44. end

(左右滑动查看全部代码)

五、项目数据库同步问题

将博客服务和认证服务分离,将引发同步问题。原因之一是,两者都需要各自保存用户信息。ExpressJS需要用到用户的身份信息,而Sinatra需要用到其他的用户信息(比如头像,个人描述以及发帖、评论数据之间的关联关系等),对于这个问题可以有多种解决方案

  • 方案一:在认证服务的用户表中保存全部用户信息。在博客服务的用户表中将仅保存用户的ExpressJS服务ID(即user_id)以用来在认证服务中索引和查询用户数据。

  • 方案二:在博客服务中不设用户表。所有涉及到用户数据的博客数据库表都将保存ExpressJS用户ID作为索引。

  • 方案三:在认证服务中仅保存身份信息(如邮件地址和密码),其余的信息保存在博客服务中。当需要在博客服务中引用认证服务的用户数据时,以用户ID或者邮件地址作为唯一索引来关联,当使用邮件地址时,需要在博客服务中同时保存用户的邮件地址。

可以按自己的实际情况从上述的方案中做出选择。我会选择第三个方案,让每个服务仅保存自己所需要的合理的数据。这样,只需要少量的代码修改,我就可以在未来的项目中复用这一认证服务,以期在Sinatra应用中充分利用Ruby的Active Record机制来进行用户关系建模和查询。要谨慎的时刻保持应用间的用户数据同步,比如,如果在ExpressJS应用中删除或者新建了一条用户信息,确保这一变更同步到Sinatra应用。


关于EAWorld:微服务,DevOps,数据治理,移动架构原创技术分享,长按二维码关注

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

基于JWT规范实现的认证微服务

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

基于JWT规范实现的认证微服务

本文由公众号EAWorld翻译发表,转载需注明出处。作者:Marcelo Fonseca译者:白小白 原题:Building an authentication micro-service with JWT standard原文:http:
2023-06-05

Flask中基于Token的身份认证的实现

本文主要介绍了Flask中基于Token的身份认证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
2023-02-20

springsecurity如何实现基于token的认证方式

这篇文章主要为大家展示了“springsecurity如何实现基于token的认证方式”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“springsecurity如何实现基于token的认证方式”
2023-06-20

Flask中基于Token的身份认证如何实现

今天小编给大家分享一下Flask中基于Token的身份认证如何实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Flask提
2023-07-05

Linux系统中SSH服务基于key认证实践的过程

众所周知ssh是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议,它默认工作在tcp的22号端口,具体实现的软件有:openssh(centos默认安装的),dropbear。ssh协议目前有两个版本v1和v2,v1基于CRC-3
2022-06-04

Java开发之spring security实现基于MongoDB的认证功能

本文实例讲述了Java开发之spring security实现基于MongoDB的认证功能。分享给大家供大家参考,具体如下:spring security对基于数据库的认证支持仅限于JDBC,而很多项目并非使用JDBC,比如Nosql数据库
2023-05-30

基于Go语言的微服务架构设计与实现

随着云计算和容器化技术的快速发展,微服务架构已经成为了构建大型分布式系统的首选架构之一。微服务架构的核心理念是将复杂的单体应用拆分成一系列小而独立的服务,通过轻量级的通信方式进行交互,从而提高系统的可伸缩性、可靠性和可维护性。而Go语言作为
基于Go语言的微服务架构设计与实现
2023-11-20

微服务中怎么通过Feign实现密码安全认证

本篇文章为大家展示了微服务中怎么通过Feign实现密码安全认证,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。微服务通过Feign调用进行密码安全认证Feign是客户端配置,@FeignClient注
2023-06-20

jizz基于构建的服务规范的模型包含两个端处理的流程

基于构建的服务规范的模型中,包含两个端处理的流程。这两个端分别是客户端和服务器端。客户端处理流程:1. 客户端发起请求:客户端向服务器端发送请求,请求特定的服务。2. 参数传递:客户端将请求所需的参数传递给服务器端,以便服务器端能够正确地处
2023-08-23

Python基于select实现的socket服务器

本文实例讲述了Python基于select实现的socket服务器。分享给大家供大家参考,具体如下: 借鉴了asyncore模块中select.select的使用方法import socket import traceback import
2022-06-04

基于Docker的Consul集群实现服务发现

服务发现其实简单说,服务发现就是解耦服务与IP地址之间的硬绑定关系,以典型的集群为例,对于集群来说,是有多个节点的,这些节点对应多个IP(或者同一个IP的不同端口号),集群中不同节点责任是不一样的。比如说一个数据集群中,可以分为读节点或者写节点,写节点和读节点
基于Docker的Consul集群实现服务发现
2015-11-26

基于python的简单HTTP服务器实现

HTTP协议请求报文请求头部字段解析响应报文响应头部字段解析响应状态码HTTP服务器实现http协议大概是我们接触的最多的协议了,每打开一个网页,浏览器和服务器之间,使用的就是HTTP协议。HTTP协议属于应用层协议,下一层是运输层。这段时
2023-01-31

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录