Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

需支持忘记密码可选择短信、微信方式。 #631

Closed
Zoeyzhou11 opened this issue Aug 22, 2022 · 20 comments
Closed

需支持忘记密码可选择短信、微信方式。 #631

Zoeyzhou11 opened this issue Aug 22, 2022 · 20 comments
Assignees
Labels
Layer: api Api module related Layer: login Login module related Priority: High Sign: feature requested New feature request todo 进入开发排期的状态,纳入了最近的迭代
Milestone

Comments

@Zoeyzhou11
Copy link

Zoeyzhou11 commented Aug 22, 2022

需求背景
当前修改密码的方式为邮箱修改,但是部分企业尚未使用企业邮箱,用户信息中填充的邮箱非真实邮箱。因此需要支持短信/微信修改密码
需求描述
1、支持短信重置
image

2、支持微信扫码重置
image

@Canway-shiisa
Copy link
Contributor

短信的优先实现

@wklken
Copy link
Collaborator

wklken commented Nov 14, 2022

  1. 可以参考原先双因子验证的代码
  2. 可以引入redis作为默认存储? (是的话, 二进制版本的配置/容器化版本的配置需要确认怎么改)

@wklken wklken added Sign: feature requested New feature request Layer: api Api module related Layer: login Login module related Priority: High todo 进入开发排期的状态,纳入了最近的迭代 labels Nov 14, 2022
@wklken wklken added this to the Y2022M47 milestone Nov 21, 2022
@wklken wklken mentioned this issue Nov 21, 2022
@wklken wklken modified the milestones: Y2022M47, Y2022M48 Nov 28, 2022
@neronkl
Copy link
Contributor

neronkl commented Dec 1, 2022

image

@neronkl
Copy link
Contributor

neronkl commented Dec 1, 2022

  1. usersettings_meta 新增源数据
{
	key="reset_pwd_sms_config",
            example={
                "title": "【蓝鲸智云】密码重置,验证码提醒",
                "sender": "蓝鲸智云",
                "content": "",
                "content_html": "",
            },
            default={
                "title": "【蓝鲸智云】密码重置,验证码提醒",
                "sender": "蓝鲸智云",
                "content": "",
                "content_html": '',
            },

}
更新已存在的local类型目录的usersetting数据中
  1. api/v1/web/passwords/reset/captcha/ 新增支持发送重置验证码

  2. 使用发送验证重置密码的功能时候后,接口返回对应用户的token

    token负责后续的captcha的校验以及密码重置时候需携带的

    {
    	code:
    	data: {
    		phone:"131xxxx8888"
    		token:
    	}
    	message:
    	result
    }
    
  3. 新增 PasswordResetSendCaptchaInputSLZ 支持多种方式

class PasswordResetSendMsgInputSLZ(...):
	phone= Charfield()
	
  1. 使用redis进行缓存验证码,时效5min

    {prefix}_reset_pwd_{token}:{
    	"captcha": "" # 验证码
    	"username": username@domain # 用户名@域名
    	"error_times": # 验证码错误次数
    }
    
  2. api/v1/web/passwords/reset/verify_captcha/

POST

request_body
{
	"token": "",
	"captcha": ""
}
=======================
reponse_data
{
	code:
	data: {
		token:
	}
	message:
	result
}
  1. api/v1/web/passwords/reset/by_captcha/

    POST

    request_body
    {
    	"token":
    	"password":
    }
    ===========================
    repsonse_data
    {
    	code:
    	data: {}
    	message:
    	result
    }
    
  2. 以上为正常执行,没有验证码校验通过的情况。当验证码错误超出一定的次数后学等待验证码失效后,才可重新进行

    # captcha
            ErrorCode("CAPTCHA_TOKEN_EXPIRED", _("验证码过期,请重新点击发送")),
            ErrorCode("CAPTCHA_WRONG", _("验证码错误,请重新输入")),
            ErrorCode("CAPTCHA_DUPLICATE_SENDING", _("验证码已经发送,请等待{expire_time}分钟后发送")),
            ErrorCode("TOKEN_INVALID", _("验证码失效 ")),
            ErrorCode("MAX_ERROR_TIME", _("验证码错误次数过多,请{expire_time}分钟后在操作")),
    

@neronkl
Copy link
Contributor

neronkl commented Dec 1, 2022

@Canway-shiisa

@Canway-shiisa
Copy link
Contributor

Canway-shiisa commented Dec 2, 2022

image
获取验证码接口:GET api/reset/verification_code/
验证码验证:POST api/reset/captcha/
======
看看接口命名 变更成这两个会不会更restful一些;
目前 发邮件验证的接口是 api/send_email,这里发短信验证也可以是 api/send_sms 保持一致?

@Canway-shiisa
Copy link
Contributor

4.这里 是想把reset/send_email/接口和通过短信验证的接口整合成一个?感觉不太合适
5.缓存时长 1min?

@neronkl
Copy link
Contributor

neronkl commented Dec 2, 2022

  1. 将发送重置验证码为独立模块,不和原先邮箱发送重置链接耦合
  2. 缓存时长需要和产品确认。

@wklken
Copy link
Collaborator

wklken commented Dec 2, 2022

1 没问题
2 协议没写, 需要手机号或username, 返回手机号? (手机号需要在后台隐藏掉中间段), 在前端手机不能是明文=> 否则安全风险, 可以随意获取某个人的手机号
3 返回token后, 校验验证码只需要 验证码+token? 不能带有手机号(隐藏掉中间段后, 大概率重复)=> 上面方案的 2/3 是一个接口? 是的话, 按协议格式写
4 入参不能是手机号
5 和 6 很奇怪, 不调用 5 接口, 直接到 6 也能成功?


正确的逻辑:

  1. 输入手机号/username => 生成一个 captcha_token (不要跟现在的reset by token的token概念重复), 同时返回 隐藏中间段的手机号 /send_captcha (老)
  2. 用户输入验证码+captcha_token 校验 /verify_captcha(新)
  3. 校验通过后, 生成一个 token (这个就是rest by token的token)
  4. 直接调用原来的reset by token接口重置密码 /reset_by_token

另外, 补充验证码处理逻辑(什么情况下更新错误次数等等)

@neronkl
Copy link
Contributor

neronkl commented Dec 2, 2022

  1. 验证码处理:调用 api/.../verify_captcha/ 通过前端传的captcha_token 在Redis中获取正确的captcha进行比对
    成功: 验证码校验成功,直接从redis中剔除(del key {prefix}reset_pwd{captcha_token}),生成profile_token返回
    失效:redis 无法 get key {prefix}reset_pwd{token} , 说明失效, return error
    失败:比对失败, 刷新Redis, key={prefix}reset_pwd{token} error_time +=1; error_time 大于默认失败次数,直接return
    error
    失败最大次数: 新增meta_key最大失败次数 default=3

  2. api/.../send_captcha/ 发送验证码 POST,生成captcha_token,captcha保存在Redis,时效:3min
    request_body:phone/username
    response_data: phone(隐藏部分号码, 类型str) & captcha_token (类型str)

  3. api/.../verify_captcha/ 校验验证码 POST,处理如第一点
    request_body: captcha(类型str) & captcha_token
    response_data: token (类型str)

  4. 前端跳转到重置页面调用原先接口 /reset_by_token

@wklken
Copy link
Collaborator

wklken commented Dec 2, 2022

error_time to error_count

@neronkl
Copy link
Contributor

neronkl commented Dec 2, 2022

captcha_token = hash()
发送验证后,Redis captcha缓存,键值对设置 captcha_token = { “captcha”: "xxxx", "error_count": 0}。

@wklken wklken modified the milestones: Y2022M48, Y2022M49 Dec 5, 2022
@nannan00
Copy link
Collaborator

nannan00 commented Dec 6, 2022

  1. captcha to verification_code 更合适

  1. 输入手机号/username => 生成verification_code并短信发送verification_code
  2. 用户输入verification_code => 校验verification_code,OK后生成Token(沿用rest by token的token)
  3. 直接调用reset_by_token接口重置密码
    相当 通过verification_code换取到token,和原来直接发送带token url的邮件之后的处理逻辑一样,即tokne获取方式不一样而已

需要注意安全问题:

  1. 不能无限制生成 verification_code,需要频控,如何实现可能比较麻烦
  2. 如何解决遍历verification_code碰撞,同时避免verification_code过于复杂导致用户输入麻烦
  • 可能在验证verification_code时需要同时输入username/phone + verification_code,并对相同username/phone进行频率控制

@neronkl
Copy link
Contributor

neronkl commented Dec 7, 2022

  1. 验证verification_code,验证成功时:redis中设置1min 缓存, 对该用户username/phone的进行绑定,限制对该用户进行verification_code的重复发送
    {username/phone}_is_send: 1
  2. 在进行发送verification_code的时候后,前置条件判断Redisz中key={username/phone}_is_send,是否存在,再进行后续逻辑处理

@wklken @nannan00 @Canway-shiisa
有个问题需确认一下:当前项目中profile,username@domain唯一,但是同一个phone可以绑定多个user。进行发送verification_code的时候,无法确认对应的用户, 输入是否可以直接为username@domain

@wklken
Copy link
Collaborator

wklken commented Dec 7, 2022

手机号是绑定关系, 目前不是全局唯一的, 如果一个手机绑定多个账号, 目前的登录逻辑是会报错的

所以

  1. 拿手机号来发短信, 是不知道他要重置哪个账号的密码
  2. 如果只输入用户名, 目前手机号是非必填的, 假设没有填, 这时候短信是发不出去的(未来手机号也是可选的)

@Shutulee @Zoeyzhou11 需要产品侧考虑下这里的交互, 可能要做修改

@Shutulee
Copy link
Collaborator

Shutulee commented Dec 7, 2022

手机号是绑定关系, 目前不是全局唯一的, 如果一个手机绑定多个账号, 目前的登录逻辑是会报错的

所以

  1. 拿手机号来发短信, 是不知道他要重置哪个账号的密码
  2. 如果只输入用户名, 目前手机号是非必填的, 假设没有填, 这时候短信是发不出去的(未来手机号也是可选的)

@Shutulee @Zoeyzhou11 需要产品侧考虑下这里的交互, 可能要做修改

1、未来多渠道登录会约定手机号在单目录下全局唯一。目前只能先给临时方案。
2、非必填的需求要求短信/邮箱二选一(必填其一),未来有了其他的联系方式会多选一,由用户自行选择可行的找回密码方式;未填的联系方式会提醒「不存在/未绑定」。**非必填和多渠道会一起设计。

临时方案:
1、通过输入的用户名定位账号,再走手机号/邮箱重置校验(现在还是必填的逻辑)。
2、只输入手机号,一个手机号绑定多个账号的场景会报错拦截,提醒「该手机号同时绑定多个账号,无法定位账号并重置密码,请输入具体的用户名或联系管理员处理」。
3、手机号/邮箱之后改成全局唯一后,存量的用户也是暂时先走上面的逻辑。
@wklken

@wklken
Copy link
Collaborator

wklken commented Dec 7, 2022

@neronkl 按照上面的逻辑处理吧

@neronkl
Copy link
Contributor

neronkl commented Dec 7, 2022

好的,请问方案是否还有其他问题?@wklken @nannan00 @Canway-shiisa

@nannan00
Copy link
Collaborator

nannan00 commented Dec 7, 2022

需要描述 verification_code生成的频率控制方案,比如1分钟内最多生成一个,1分钟后再次点击的话,应该如何处理,1天最多点击多少次,不能无限制的发送验证码的短信

@neronkl
Copy link
Contributor

neronkl commented Dec 8, 2022

   redis缓存数据
   verification_code_token = hash(username+phone)
   
   # 验证码缓存
   reset_password_{verification_code_token} : {
   	verification_code: "",
   	error_count: 0
   	timestamp:生成时间
   }
   # 当日发送次数缓存 定时每日0点过期
   reset_password_send_count_{phone}: 1

频率限制方案:
​ a. 针对reset_password_{verification_code_token}在redis中时效为5min,相当于需间隔5分钟才能再次发送,还在时效内,return error。超过5min该条缓存记录已经清除,才可再次发送
​ b. reset_password_send_count_{phone}记录当日该手机发送次数,但超过当日最大发送次 数,return error。否则++1

@nannan00

@Xmandon Xmandon modified the milestones: Y2022M49, Y2022M50 Dec 12, 2022
caohua pushed a commit to caohua/bk-user that referenced this issue Dec 15, 2022
wklken pushed a commit that referenced this issue Dec 16, 2022
* feature: 支持密码重置可选择短信验证方式 #631
wklken added a commit that referenced this issue Dec 16, 2022
wklken pushed a commit that referenced this issue Dec 16, 2022
@wklken wklken modified the milestones: Y2022M50, Y2022M51 Dec 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Layer: api Api module related Layer: login Login module related Priority: High Sign: feature requested New feature request todo 进入开发排期的状态,纳入了最近的迭代
Projects
None yet
Development

No branches or pull requests

7 participants