美文网首页
身份认证的几种方式

身份认证的几种方式

作者: 今夜秋风和 | 来源:发表于2022-12-02 20:44 被阅读0次

Http 基础认证

通过向服务端发送用户名和密码(http header 中增加 Authorization key ,value 为username:password 的Base64 进行编码后进行认证的一种方式),如下图:


截屏2022-11-27 上午8.58.32.png

交互流程:

1.客户端访问登录接口 http://127.0.0.1:5000/flask/students/login

截屏2022-11-27 上午8.54.52.png
2. 服务端接口上使用@auth.login_required 标明需要登录认证,当请求进入时,会调用@auth.verify_password 装饰的verify_password(user_name_or_token,password)函数,这里以flask 接口为例
@students.route('/login')
# 访问路由需要登录
@auth.login_required
def test_index():
   if  g.current_user is None:
      return jsonify({'code': 401,'message':'用户密码验证未通过'})
    stu = {
        'id': 0,
        'name':'zxl'
    }
    return jsonify({'data':stu})

@auth.verify_password
def verify_password(user_name_or_token,password):
    if user_name_or_token == '' or password == '':
        return False
    # 基于用户名 & 密码验证
    user = User.query.filter_by(name = user_name_or_token).first()
    if not user:
        return False
    else:
        # 获取到用户信息,全局对象中设置current_user
        if user.verify_password(password):
            g.current_user = user
            return True
    return False
  1. 针对以上的验证密码的流程,当未输入用户名或密码时,或者不存在当前用户以及 密码认证不通过时,返回false ,服务端认为认证不通过时,返回401 未授权

4.用户带上正确的用户名和密码,请求接口


image.png

缺点

  验证方式简单,但用户的密码被直接暴露,存在风险
  密码被恶意截取后,造成重放攻击

Http 摘要认证

是另一种http 认证协议,试图修复基本认证的缺陷。改进点:

  • 客户端通过发送摘要,而不是用户名,密码,解决密码暴露的风险;
  • 防止恶意用户捕获并重放认证的握手过程,使用随机数,客户端使用用户名,密码和随机数生成新的摘要;
  • 防止对报文内容的篡改;
  • 防范常见的攻击形式;

摘要认证过程

1.客户端请求服务端 http://127.0.0.1:5000/flask/students/login_digest

截屏2022-11-28 上午8.34.25.png

2.服务端计算随机数,以及支持的算法列表发到客户端, response header 中WWW-Authenticate 字段


image.png 截屏2022-11-28 上午8.59.45.png

3.客户端输入用户名,密码,通过算法根据用户名,密码,随机数等生成摘要,然后将摘要放在Authorization首部中发送到服务器


截屏2022-11-28 上午8.52.16.png

Authoriaztion 首部字段:


截屏2022-11-28 上午8.52.38.png

4.服务端进行通过客户端提交的随机数和存储在服务器中的密码生成摘要,进行比对,如果一样,则认证通过,如果客户端用自己随机数对服务器进行质询,就会创建客户端摘要,服务端可以预先将下一个随机数计算出来,下发给客户端;

截屏2022-11-27 下午10.14.59.png

摘要认证各个首部参数含义

WWW-Authentication:定义使用http的哪一种方式(Basic、Digest、Bearer等)进行认证来获得受保护资源
realm:表示Web服务器中受保护文档的安全域,用来指示需要哪个域的用户名和密码
nonce:服务端向客户端发送质询时附带的一个随机数,每次401 会产生一个新的随机数,用来避免重放攻击
qop:安全保障值, 值为auth 表明要进行认证,值为auth-int 表明要进行完整性认证
nc:nonce计数器,是一个16进制的数值,表示同一nonce下客户端发送出请求的数量。例如,在响应的第一个请求中,客户端将发送“nc=00000001”。这个指示值的目的是让服务器保持这个计数器的一个副本,以便检测重复的请求
cnonce:客户端随机数,这是一个不透明的字符串值,由客户端提供,并且客户端和服务器都会使用,以避免用明文文本。这使得双方都可以查验对方的身份,并对消息的完整性提供一些保护
response:实际的摘要,以证明用户知道密码

Authorization-Info:用于返回一些与授权会话相关的附加信息
nextnonce:下一个服务端随机数,使客户端可以预先发送正确的摘要
rspauth:响应摘要,用于客户端对服务端进行认证
stale:当密码摘要使用的随机数过期时,服务器可以返回一个附带有新随机数的401响应,并指定stale=true,表示服务器在告知客户端用新的随机数来重试,而不再要求用户重新输入用户名和密码了

摘要的计算

1.安全性相关数据A1
A1=<user>:<realm>:<password> (MD5算法)
A1=MD5(<user>:<realm>:<password>):<nonce>:<cnonce>(MD5-sess算法)
2.报文相关数据
A2=<request-method>:<uri-directive-value>(qop未定义或者auth)
A2=<request-method>:<uri-directive-value>:MD5(<request-entity-body>)(qop为auth:-int)
3.计算摘要response 字段
response=MD5(MD5(A1):<nonce>:MD5(A2))(qop未定义)
response=MD5(MD5(A1):<nonce>:<nc>:<cnonce>:<qop>:MD5(A2))(qop = auth)
response= MD5(MD5(A1):<nonce>:<nc>:<cnonce>:<qop>:MD5(A2))(qop=auth-int)

flask 摘要认证的案例

auth  = HTTPDigestAuth() # 摘要认证
# 摘要认证请求接口
@students.route('/login_digest')
# 访问路由需要登录
@auth.login_required
def test_index_digest():
    stu = {
        'id': 0,
        'name':'zxl'
    }
    return jsonify({'data':stu})
#密码验证
@auth.get_password
def get_pw(user_name):
    user = User.query.filter_by(name=user_name).first()
    if user is not None:
        # 模拟在服务器中存储密码
        return user.password
    return None

由于支持http协议的浏览器已经实现了生成摘要的相关步骤,所以通过以上可实现摘要认证的过程(稍后在服务端类库源代码中可以看到摘要认证的相关实现)

令牌token认证

基于http 协议的通信本身是无状态的,也就是用户的每次请求之间是互相独立的,系统为了在同一个用户的多次请求之间建立关联关系,即对用户数据的共享,传统的解决方案利用cookie,session 机制,但这样存在一个缺点是服务器需要维持大量session ,势必会影响服务器性能。token 机制解决这个问题,它通过针对每一个用户生成一个带有过期时间的标识,用户再下一次请求header 中带上即可,服务端通过解析token, 可以知道用户身份,flask 中itsdangerous 库通过TimedJSONWebSignatureSerializer 类实现了token 的生成与验证;

我们模拟下前后端的交互流程:
1.用户首次通过用户名,密码进行登录;
2.服务端密码验证通过后利用用户id 生成token,下发到客户端;
3.客户端携带token 请求获取用户信息;
4.当token 在服务端验证发现过期时,给客户端进行提示,客户端跳转到登录页面,输入用户名,密码继续2的流程;

class User(db.Model):
    __tablename__ = 'user_account'
    id = Column(Integer, primary_key=True)
    name = Column(String(30))
    fullname = Column(String(30))
    # 生成用户token
    def generate_auth_token(self,expiration):
        s = Serializer(config['dev'].SECRET_KEY,expires_in = expiration)
        token = s.dumps({'id': self.id})
        print('token',token)
        return token

    @staticmethod
    # token 验证
    def verify_auth_token(token):
        s = Serializer(config['dev'].SECRET_KEY)
        try:
            data = s.loads(token)
        except Exception as ex:
            print('ex', ex)
            return None
        return User.query.get(data['id']) is not None
 # 和basic 基础认证配合,密码认证通过后生成token
@students.route('/tokens', methods=['GET'])
# auth = HTTPBasicAuth()
@auth.login_required 
def get_token():
    if g.current_user is None:
        return auth_error()
    return jsonify({
        'token': g.current_user.generate_auth_token(expiration=7200),
        'expiration': 7200
    })
 # 基于token 认证通过后获取用户信息
@students.route('/get_user_info', methods=['GET'])
# auth = HTTPTokenAuth()
@auth.login_required
def get_user_info():
    if g.current_user is None:
        return jsonify({'code':401,'message':'token认证不通过'})
    stu = {
        'id': 0,
        'name': 'zxl'
    }
    return jsonify({'data': stu})

# token 验证
@auth.verify_token
def verify_token(token):
    s = Serializer(config['dev'].SECRET_KEY)
    try:
        data = s.loads(token)
    except SignatureExpired as ex:
        print('SignatureExpired', ex)
        return False
    except BadSignature as ex:
        print('BadSignature', ex)
        return False
    if User.query.get(data['id']) is not None:
      g.current_user = User.query.get(data['id'])
      return True
    return  False

实现过程

flask_httpauth 中模块这几种实现方式基本类似,父类HTTPAuth 下分别有HTTPBasicAuth,HTTPDigestAuth,HTTPTokenAuth 这几个不同的实现类;
基本认证主体流程:
需要验证的接口上统一带有这个@auth.login_required,login_required 是什么,顺着代码进入,可以看到是父类HTTPAuth 中一个函数


image.png

在用户请求时,先调用这个login_required() 方法,在self.get_auth()中如果是基本认证,则base64 decode 获取用户用户输入的用户名,密码


image.png
通过返回的Authorization 对象和取出的密码,通过user = self.authenticate(auth, password) 来认证,在HTTPBasicAuth 类中,使用self.verify_password_callback 回调函数来验证密码
image.png
那么这个回调函数又是什么,就是我们的验证方法带有@auth.verify_password 所标注的方法,当我们点击@auth.verify_password 进入时,会跳转到以下的代码, 所以程序会执行到我们自己的验证逻辑上;
def verify_password(self, f):
      self.verify_password_callback = f
      return f

同理,token验证方式的核心部分

    def verify_token(self, f):
        self.verify_token_callback = f
        return f

    def authenticate(self, auth, stored_password):
        if auth:
            token = auth['token']
        else:
            token = ""
        if self.verify_token_callback:
            return self.ensure_sync(self.verify_token_callback)(token)

digest 认证的核心部分,和理论中的算法实现保持一致,有一点是摘要认证的密码需要保存在服务端,用于生成摘要和header Authorization 中reponse 进行比较;

    def authenticate(self, auth, stored_password_or_ha1):
        if not auth or not auth.username or not auth.realm or not auth.uri \
                or not auth.nonce or not auth.response \
                or not stored_password_or_ha1:
            return False
        if not(self.verify_nonce_callback(auth.nonce)) or \
                not(self.verify_opaque_callback(auth.opaque)):
            return False
        if auth.qop and auth.qop not in self.qop:  # pragma: no cover
            return False
        if self.use_ha1_pw:
            ha1 = stored_password_or_ha1
        else:
            a1 = auth.username + ":" + auth.realm + ":" + \
                stored_password_or_ha1
            ha1 = md5(a1.encode('utf-8')).hexdigest()
        if self.algorithm == 'MD5-Sess':
            ha1 = md5((ha1 + ':' + auth.nonce + ':' + auth.cnonce).encode(
                'utf-8')).hexdigest()
        a2 = request.method + ":" + auth.uri
        ha2 = md5(a2.encode('utf-8')).hexdigest()
        if auth.qop == 'auth':
            a3 = ha1 + ":" + auth.nonce + ":" + auth.nc + ":" + \
                auth.cnonce + ":auth:" + ha2
        else:
            a3 = ha1 + ":" + auth.nonce + ":" + ha2
        response = md5(a3.encode('utf-8')).hexdigest()
        return hmac.compare_digest(response, auth.response)

以上这几种不同的认证实现方式本质上都是通过回调函数来执行用户具体类型信息认证,当认证通过后(返回False/True)来决定接口最终的response响应;
以上如有不正确的地方,欢迎大家一块交流!

相关文章

  • SKIL/开始/身份认证

    身份认证 根据你使用SKIL的方式,始终需要身份认证。有几种方法可以通过SKIL获得身份认证,本文档将指导你完成每...

  • 实名认证方案

    实名认证有三种,一种身份证实名认证、手机号实名认证、银行卡实名认证。下面我们详述这几种认证方式的要素及接口。 1、...

  • [接口测试_B] 11 requests的身份认证方式

    requests提供多种身份认证方式,包括基本身份认证、netrc 认证、摘要式身份认证、OAuth 1 认证、O...

  • [转载]公有云API的认证方式:AK/SK 简介

    1 公有云API的认证方式 一般有以下几种认证方式: Token认证 AK/SK认证 RSA非对称加密方式 下面主...

  • ElasticSearch | 集群身份认证 | 用户鉴权

    身份认证 | Authentication 认证体系的几种类型 提供用户名和密码 提供秘钥或 Kerberos 票...

  • 总结

    身份认证 身份认证的基本方式基本方式可以基于下述一个或几个因素的组合所知(Knowledge):即用户所知道的或所...

  • Shiro权限管理笔记

    一.用户身份认证 身份认证,就是判断一个用户是否为合法用户的处理过程。最常用的简单身份认证方式是系统通过核对用户输...

  • Cookie、Session、Token、RefreshToke

    HTTP 是无状态的协议,服务端无法确认访问者的身份。 身份认证的方式有: Authentication 认证,如...

  • Shiro授权和认证流程

    认证流程 认证过程: 身份认证,就是判断一个用户是否为合法用户的处理过程.醉常用的简单身份认证方式是系统通过核对用...

  • OAuth2 概念学习

    2020-6-1学习 认证(登录) 定义用户认证就是判断一个用户的身份是否合法的过程 身份认证方式用户名密码登录、...

网友评论

      本文标题:身份认证的几种方式

      本文链接:https://www.haomeiwen.com/subject/oqsixdtx.html