点验在app注册app登录验证场景时,用户使用非本机手机号码

注册 | 登录
一起学习《设计心理学》课程讲师/68RdWg
专为互联网人打造的365天成长计划,500门视频课程随便看,构建你的产品、运营知识体系。
最近换了个手机号(没错,为了躲避债主带走我的喜儿),换手机号除了要通知通讯录里的朋友们,少不了要给众多绑定了旧手机号的产品们更换手机号。不得不说这是一项大工程。幸好旧手机号还能用一段时间,不然会更麻烦。
下面我就更换手机号这一服务来八一八我所使用部分有代表性产品的这个设计。我从安全性 和方便性两方面进行评价。以下分类并不严格,一个APP可能会跨分类。
体验时间:到02-26
产品版本:都是体验时间当下的最新版本
平台:iOS、网页
一、折腾人!不能在App里修改手机号
1.1不能躺着改,电脑网页走起
小米智能家庭
必须登录小米官网修改。小米智能家庭APP里的个人中心,主要功能围绕已有设备的扩展和维护,比如设置智能场景、共享设备、固件更新等。对于一个不经常使用的换手机号这样的功能不放在这里是能接受的。但是希望“个人中心”能换个名字,这个名字总觉得在暗示我可以修改个人信息。
后面再次提到时评星。
除了要登录网页修改手机号,这个APP还有更大的槽点,后面一起吐槽。
虽然不能在APP改,但是他的解绑方式也算独特,后面细聊。
1.2不能宅在家,营业厅走起
中国银行、招商银行、建设银行APP
银行预留手机号,老老实实拿着身份证去营业厅排号吧。
分析:考虑到现在电信诈骗手段层出不穷,本人持身份证去修改是可以理解的,但是四大银行的排号真心太浪费时间。希望能推出预约或者网上排号查看进度之类的服务,也算是互联网+
思维的体现吧。
安全性:5星
方便性:0星
二、爱死你了!无需验证旧手机号
无需使用旧手机获取验证码,直接输新手机号和验证码就能改。更换手机号码后,试了旧手机号验证码登陆发现:旧手机号账号已经不存在了,想要使用必须重新注册。
分析:考虑到部分用户信息中的订单、优惠、积分信息,感觉这样其实不够安全。修改密码也需要先验证手机号,如果有人通过我的手机修改了绑定的手机号和密码,在我发现异常并改成自己的手机号之前,“有人”是可以通过其他设备看到我的资料的。
建议能对修改者进行简单的身份判断,避免恶意或者无意的操作带来的安全隐患。
安全性:2星
方便性:5星
直接验证新手机即可。
分析:区别于下厨房,支付宝在打开APP时需要输入指纹或者手势,这其实就是进行精确的身份识别。
身份识别通过后,可以认为进入支付宝的用户是账号所有者或者非常亲近的人。更换手机号直接验证新手机号避免了多余的认证过程。
安全性:4星
方便性:5星
三、只能接受了。。。必须验证旧手机号
3.1 全家集享卡
申诉都木有啊,如果不行旧手机号不能用,只能去店里问店员了。全家是故意的吧,好让大家顺便消费一下。。。
分析:这样的设计让用户有种无力感和失控感,完全没体现出人性化设计的原则。
安全性:5星
方便性:0星
四、看运气–不需要旧手机号也能改
旧手机号不能使用的情况下能否快速修改成功看人品喽。运气好,碰到考虑周到、流程合理的产品真是有种捡到宝的感觉。运气不好就只能呵呵了。
4.1硬件/申诉
旧手机号不能接收验证码情况下,小米提供的解决方案。
分析:比全家集享卡相比至少提供了解决方法,但是这2种方案看起来完全不熟悉、很麻烦又充满了不确定性,用户真的喜欢吗?
安全性:4星
方便性:2星
4.2 验证密码
美团提供了两种验证方式,短信和支付密码。前提是已绑定银行卡设置支付密码。
分析:两种验证方式,给了用户更多的选择余地和容“错”空间。允许用户旧号码不能使用,或者选择自己使用起来更顺手的验证方式。
安全性:4星
方便性:3星
输入密码解除绑定,旧手机号收到解除绑定相应账号的通知短信。再回到账号管理页绑定新手机号。
分析:验证登录密码,避开了旧手机号已经不能使用的问题。但是要记好自己密码或者用工具管理好自己的密码,否则要先找回密码也是很麻烦的。如果账号只绑定了手机号,手机号又不能使用,就会陷入死循环。。。
安全性:4星
方便性:3星
4.3 功能性短信
直接验证新手机号,但是需要进行二次安全验证。二次安全验证只需输入旧手机号,不需要验证码,然后旧手机号收到带有撤销功能短信。
分析:使用可撤销短信的方式取代用户输入验证码的操作,对多数真心想修改手机号的用户来回操作是比较简单的。
而且,输入旧手机号要比输入验证码要方便的多。旧手机号收到可撤销的短信也比较安全。恶意修改的话自己可以撤销,旧手机号不能用的情况下自己还能绑定新手机号。
安全性:4星
方便性:4星
五、天了噜,新手机号已被前主人注册了
输入新手机号后提示该手机号已注册。
登录网站找到在线客服,发现里面根本没有关于账号问题的客服。只能申诉了,反馈速度倒是挺快的,两个电话解决了。方法是注销新手机号的帐户。注销前我通过找回密码功能,妥妥的进入了新手机号帐户,偷窥了一下,别人的个人信息就这样暴露了。。。
分析:这个亲身经历的过程给我的反思是
有必要处理好已弃用帐户的用户信息。对于用户来说,更换手机号要记得处理好自己的账户们。对于产品来说,只使用手机号注册的确快捷方便,但是安全隐患也不小,尤其是会涉及到个人真实信息和金钱交易的产品。
在这个案例中,如果能在填写替换手机号并输入正确验证码之后,提示“这个手机号已注册如不是本人注册请点击确认替换,原账号将被注销”,用户可选择确认替换或者取消。这样可以保护新号以前主人的个人信息,也保证了新主人的使用权利。
安全性:2星
方便性:1星
六、可怜见,旧手机号的新主人就别想注册这个产品了
先点击“更过”
进入挖财账号发现了绑定手机的信息,但是没有修改按钮。进入设置看到可以修改密码的地方,同样没看到修改手机号的地方。
联系在线客服,在线客服是机器人(其实有人工,后来才发现),问题没有得到答案。
直到在帮助与反馈中看到“如何更改绑定的手机号?”得知要登录网页修改。
按照APP里的提示在网页修改了绑定的手机号,但是账号依然是原来的手机号。网页联系在线客服,默认机器人,但是看到了人工服务的字样,立马人工服务用起来,得知挖财账号的唯一性,既不能修改,明确得到回复:旧手机号的新主人不能用这个号码再注册。只能同情新主人了。。。
分析:回过头来看看APP里的在线客服页面,在左下角有个神奇的按钮(⊙o⊙)哦,点击进入人工服务。藏得有点深啊。关于客服部分我的建议是:要么在按钮上文字吧,要么在顶部nav bar设置机器人和人工的segmented controller。总之对于第一次进入这个页面又心情急躁的用户来说,文字最能传情达意了。
直接把手机号作为用户账号完全可以接收,但是不能修改或者是在用户看来不能修改这样的设定是考虑不够全面的。一是用户会质疑是否已经修改成功,二是这个手机号无法再次注册实在是产品自己给自己挖坑。
ps:已在网页修改手机号两天,APP绑定手机那里依然显示旧手机号,呵呵。
安全性:4星
方便性:2星
七、“奇葩”们
冲着用户给你发短信这点,你上榜了。
其实不需要验证旧手机号噢。
分析:其实不需要验证旧手机号。但是12306选择了支付宝类似的方法,在打开APP后需要进行身份识别,这里是需要重新登陆。通过重新登陆的方式提高了安全性。但是如果勾选了记住密码,可以直接按登录按钮登录,安全性依然堪忧。
安全性:4星
方便性:3星
7.2 备用手机号什么鬼
名片全能王
先添加备用手机,将备用手机改为主手机,回到备用手机列表,删掉旧手机号。
加备用手机号时,默认手机区域是美国(+1),记得手动滑到底改为+86,否则永远收不到验证码%&_&%。
删除备用手机号需要输密码。shit又忘了密码了。。。
分析:以上修改步骤的思维方式其实并不是其他APP的“覆盖”,而是“先加后减”加减的对象不同。第一次接触需要稍稍适应一下,增加了学习成本。
安全性:5星
方便性:2星
7.3平安人寿
联系你的代理人,请他提交申请。新旧手机号都收到待确认短信,回复同意或
者不同意,不回复的话默认不同意。
分析:其实这样的方式有些回归有网络以前的状态,想办事去找人。也有点像现在的人工智能语音助手siri一类。用户只需说“指令”。对于用户来说是我认为这其实是一种更简单、更人性化的方式。
安全性:5星
方便性:5星(对于有社交障碍的人来说可能是0颗星)
7.4 微信理财通
我的微信账号不是手机号,所以严格来讲这里说的是理财通。先把钱都转到银行卡,删掉安全卡,去银行改新的手机号,回到理财通重新绑定银行卡,才能把理财通里真实信息的手机号改成新的。这是打客服电话问到的方法。。。
分析:手机号能收到交易验证码,在这里相当于去办网银得到的网银盾这类安全凭证。同样涉及金钱交易,支付宝做的却要简单的多。毕竟支付宝作为独立APP,能调动的资源更多,理财通作为微信的一个H5应用,首先进入相关页面就少了身份验证的步骤。
安全性:1000星
方便性:-1000星
7.5 网易理财
还是换个网易账号吧,打客服电话连人工服务都木有啊,摔!
安全性:“破表”
方便性:“破表”
以上这些方案总结的并不全面,基本不同的产品都会结合自身的业务特点进行这个功能的设计。没有绝对的对错,只有合适不合适。我在这里列出比较有代表性的例子,希望能对有需要的朋友提供参考,不对的地方请各位大神批评指正哈。
ps:折腾这一回,深刻体会到了第三方账号登陆的便利性(不要让我第三方登录后再绑定手机号!)。是的,杀死你的可能是“跨界”的存在。
本文由 @darcy
原创发布于人人都是产品经理 ,未经许可,禁止转载。
如果觉得写的不错,欢迎打赏支持
收藏已收藏 | 368赞已赞 | 21
一起学习《设计心理学》课程讲师/68RdWg
产品经理群运营交流群求职招聘群
Axure交流群
PM要学点技术
关注微信公众号
10个回答15人关注
0个回答0人关注
8个回答27人关注
11个回答10人关注
9个回答85人关注
14个回答21人关注注册 | 登录
湖湖,途牛UED
专为互联网人打造的365天成长计划,500门视频课程随便看,构建你的产品、运营知识体系。
从注册这点小事儿上看如何提升细节设计和用户体验,希望对一些新入行的童鞋有所帮助。
互联时代的产品都离不开:
直白点就是什么样的环境下?为哪些人?提供了哪些事情的解决方案?
PC:室内的+相对沉溺的+长时间的
Mobile:随时随地的+碎片化的+随时转身离开的
产品定位的受众人群,可以是学生、孕妇、求职、买卖……,比如招聘类的目标受众就是需要找工作的群体;房租中介类就是需要租赁、买卖房屋的群体等等。
帮助这些受众完成他们想要得到事务的一种逻辑流程。
比如我们可以在某宝上:搜索想要的商品-锁定-下单-收货-评价,这就是一套购买流程,提供了用户网络购买流程的解决方案。
这里我们就用户注册这一场景,浅谈设计任务及其背后对思维逻辑,然后去提升细节设计。
到达注册页面的途径
用户到达一个app的注册页面一般有4种渠道:
当网易音乐的第一批用户从豆瓣平移过来之后,用户就不再是大家的了。赢取一个新用户比维护一个老用户的成本要高很多,所以当用户来到了注册界面作为设计师就要尽可能到创造舒适的用户体验,尽可能多的帮助用户注册登录。
信息构架及任务流程
就是包涵哪些信息,这些信息是怎样穿插在一起的。
注册模块概括来说由2部分组成:用户信息+验证。用户信息包括但不限于:
然后再由不同产品的定位决定类别的从筛选到简化。
验证码:是一种区分用户是计算机还是人的公共全自动程序,防刷、防黑、防灌水之类的程序行为。
被广泛采用的有:短信验证码、动态验证码组成。
可以分为逻辑流和页面流,逻辑流就是数据逻辑针对开发编写,页面流可理解为页面的跳转关系针对用户。
简单以去哪儿的注册流程中的输入手机号和验证码为例:
下面是我们呈现给用户操作的页面流
下面是介于页面流的逻辑判断
控件的状态
注册场景下涉及的控件一般是:输入框、按钮、验证码。
供用户输入信息,一般包含:提醒文案、正文、示措提醒
引发即时操作,即当用户点击后,触发即时操作。作为基础控件之一,按钮广泛应用于不同平台的所有产品中。通常位于一个或一系列操作的底部或右方。
按钮的一般状态:
PC:normal、hover、down(pressed)、disabled
Mobile:normal、onclick、disabled
这里简单说一下disabled。从去哪儿注册的例举可以看出,当input为空时,关联按钮为disabled的状态,这是采用了放错的原则:如当使用条件没有满足时,常常通过使功能失效来表示(一般按钮会变为灰色无法点击),以避免误按。
产品中的辩证法与相对论
学会用辩证法,去分析问题。在产品道路上同样适用,有时候得到就意味着失去,用辩证法的方法看问题,利于分析事情的二面性,衡量其利弊,进而找到真正适合自己产品的合适的方案。
拿动效举个例子,动效本身并没有错,但是一个庞大的产品就不宜使用过多的动效,原因:
因为动效会加重系统负担;
为了不断延长其生命力需要不断的版本迭代,很可能一些辛苦设计开发出来的动效随着产品的发展方向而被很快淹没;
最为重要的一点,对于用户而言动态的更易于吸引注视,动效的增加会使原本复杂的页面更加复杂化,更难于找到页面的重点内容而失去操作方向,事实上这个时候动效已经转变为一种打扰。
很遗憾,这个观点没有找到合适的案例进行分析,淘宝、京东、美团等在动效方面都表现得比较安静。
下面是一个天气app的页面,因为app本身很简单,就是展示天气而已,动效的增加不仅动态的解释了天气的状况也让原本单调的页面多了一丝趣味,而用户也并不会因为动效的加入而迷失。这就是相对论,简单的事务可以做些复杂的处理。
同样是列表,淘宝为什么在页面中没有采用一些动态的展示or特效,设计欠缺?开发不够?答案是否定的。淘宝营造的购物场景应该是尽量沉溺于页面流中的,尽快的找到用户想要的商品,顺利的转化购买,相信这也是作为电商的本质目的。复杂的事情简单化。
下面我们来欣赏一些按钮的微交互
设计背后的逻辑思维
随着时间流逝,科技发展,人们对事物认知不断提高,注册场景下的设计也不断进步。揣摩变化,思考逻辑,提升细节设计。
(请忽略内容,仅观察表现形式)
首先明确的是,注册页面被设计出来的目的是希望用户能够填写信息并成为我们的用户。但是,当用户看到这样的一张信息较多的页面,视觉压力和逃避心理就来了。
除了视觉和心理压力,我们还可能会被每一行的必输提醒而吸引,不过行行必输,行行提醒有点过,视觉干扰也就来了。
费茨(Fitts)定律告诉我们,当我们需要观察的2个事物距离越远时,所需要的时间就越长。
人眼阅读的轨迹顺序从1-8,这种排列方式受标题字符数的制约。有了排序参考,大家就可以考试是不是还有其他更好的排列方式?
相对于上面的注册信息,这种拆解信息的方式不管是视觉还是心理感受都会一些,而且对用户必填信息进行了提炼。这种方式后也被应用于移动端。但是拆解步骤不宜过长,否则用户会失去耐心而中途放弃注册,这是我们不愿看到的。
移动互联期
从人随网走到网随人动,用户体验收到前所未有的重视,涌现出大量的专业设计和体验设计师。对于产品注册信息的提炼由多样逐渐走向结构简单、信息简洁,以用户为中心的设计越来越被更多人接受。
下面来看下别人家的注册页面及设计思路。
淘宝的注册流程相对来说是比较长的。必填项:手机号、验证码、会员名、密码*2,有防错机制,有号码&用户名2次确认。但是第一次支付密码输入页面没有自动弹出键盘。不过整个跑下来还是挺轻松,复杂的事情简单做,困难的事情分解做。
携程的注册流程较简单,必输项:手机号、验证码、密码。无防错机制。2/3中有已输号码再现,不过却隐藏了中断部分。揣测隐藏的号码的目的是保护用户信息不受偷窥?不过以个人愚见,在注册环境,保证用户正确的输入信息,快速的完成注册才是NO.1的任务,所以这里的防保护机制会有些买椟还珠的感觉。
还有一点,激活输入页面时,键盘都没有弹出,需要点击input框调出键盘。
但是提示文案相比下引导性较强。
美团的注册流程里,在电话输入上作了放错,然后就后面就没有然后了。有防刷机制,比如当连续输入2次经系统检测为已注册的用户时,会弹popup要求人or机验证。对于输入号码有部分显露。注册第3部界面中的小尖头有误导用户后面仍有流程的误导。
去哪儿体验是相对好的,必输项:手机、验证码、密码*2,有防错机制,有密码6位数提示展示。
苏宁的注册流程,必须项:手机号、验证码、密码,有防错机制,但是允许你同样的号码再次注册,代价就是抹掉所以数据,从v5变成v0。这样来看,对于形同手机注册的提示文案还是不够的。
下面分享一下个人小案例及背后的思考,场景:注册;输入项:手机+验证码+注册btn
当时的第一版就是左侧的,也是比较保守的,不出错的。视觉线:填写手机号-看一眼验证码输入框-点击验证码按钮-回来输入验证码-点击注册。在这个过程中,验证码输入框被2次阅读,当时觉得这是一种浪费,所以出了右边的方案。视觉线是直了:输入手机号-点击验证码-输入验证码-点击注册,但是从手机号到验证码跳了一行,思路会突然断一下,再着一按钮直接冲在最前吗有点突兀。
那怎么样才能视觉线和思路保持一致呢?发现仅是一小步,将验证码按钮上移到输入手机一行,这样在用户输完手机号码时,看到验证码很自然就会点击,然后输入完成操作。当然“除非有更好的选择,否则就遵从标准”。
说到手机号码,有意看了下苹果在电话数字上的处理,虽然比起银行卡只有11位的数字,还是在不同场景下都做了分段处理,不得不说苹果的体验越来越细腻。当然这是更多的为了识别他人的号码。
虽然用户输错自己号码的几率是比较低的,但是一般来说,用户输入完毕后都会快速秒扫检查号码的正误,如果我们采用了分段的现实方法,识错上应该会容易些。
看了这么多案例,那么我们在设计注册页面的时候怎么去思考呢?
了解设计场景,设计目的。也许我们投入了很多资源做引流,当用户到达这个页面时,想尽办法留住他们。
学会站在不同产品的立场分析别人的设计思路,而不仅仅是视觉表现。
视觉简单,逻辑通畅。
目前大部分的注册流程为分解的形式及一站式,结合产品实际场景选择适合自己产品的,尽可能的简化信息、缩短流程,提升注册达成率。
对于交互对象的多层次视觉呈现,比如文案提醒、示错、验证提醒,按钮的响应反馈,都是提升用户体验的小细节。
在对象对齐及排列方式,学会依靠一些定律,法则,可以是我们在迷茫不定的时候,看轻方向。
不断学习、思考、对比,提升自己对事务的认知。
只有理解设计背后的思绪才能提升细节设计,也只有明确了设计场景和设计目的才能在不同的设计方案中找到最合适的那个。
就像买一件衣服,我们总会盯着线头、走线、针眼、纽扣、锁边这些点以此来评价一件衣服的品质,界面已是如此。经得起推敲的设计才是好设计。
本文仅笔者观点,不到之处欢迎大家沟通指正。
最后分享一下笔者在设计过程中的思考三步法则:
为什么要做这个事情?
怎么去做?
还有没有更好的解决方法?
“小是最伟大的关怀”2016和大家共勉。
作者:湖湖
本文由 @湖湖 原创发布于人人都是产品经理。未经许可,禁止转载。
如果觉得写的不错,欢迎打赏支持
收藏已收藏 | 482赞已赞 | 46
湖湖,途牛UED
产品经理群运营交流群求职招聘群
Axure交流群
PM要学点技术
关注微信公众号
10个回答15人关注
0个回答0人关注
8个回答27人关注
11个回答10人关注
9个回答85人关注
14个回答21人关注登录和oauth机制 - CSDN博客
登录和oauth机制
1.1 javaweb中如何去维持登录状态
登录后信息放入 session中
页面内验证session中是否有登录信息
如果有,不需要再次登录
如果没有,跳转登录页面
如果登录后点击注销,删除session中登录信息,并清除页面缓存(必要的)
1.2 javaweb中哪些情况我们的session会过期
过期–&很长时间没有去访问网站
主动关闭–&用户注销
切换浏览器
1.3 手机端如何维持登录状态
通过sessionKey/tokenKey
1.3.1 sessionKey/tokenKey哪里来?
登录成功之后,后台返回
1.3.2 sessionKey/tokenKey生成有什么规则?
后台返回的,按照一定规则生成的(比如可以随机数生成一个24位以上的字符串)
1.3.3 登录成功返回的sessionkey/tokenKey存到哪里?
保存到sp中就可以了
1.3.4 sessionkey/tokenKey使用场景
有些协议需要用到登录信息,就需要看登录状态,就需要用到sessionkey/tokenKey,比如支付协议
1.3.5 如何使用sessionkey/tokenKey?
需要登录状态,判断sp中是否有sessionkey/tokenKey?
有:那当前是已登录状态,就把个人信息和sessionkey/tokenKey上传到服务器;
没有:跳到登录界面,让用户登录
1.3.6 谁去判断sessionkey/tokenKey是否过期?
任何协议把sessionkey/tokenKey传到服务器.服务器会判断sessionkey/tokenKey是否过期?
过期:告知客户端,登录状态已过期,需要重新登录
未过期:可以使用当前的登录信息,继续走逻辑
1.3.7 为什么判断是否过期需要后台做?
因为前端可以修改当前时间?
1.3.8 sessionkey/tokenKey多久过期?
这个具体看公司
1.3.9 后台如何去判断sessionkey/tokenKey是否过期?
分配sessionkey/tokenKey的时候记录时间
判断是否存在对应的sessionKey:如果不存在,是不是直接就是无效.
某一时刻,用户判断sessionkey/tokenKey是否过期的时候,拿着当前时间和sessionkey/tokenKey分配时间做比较
大于指定时间:过期
没有大于指定时间:未过期
2. logindemo
public class MainActivity extends Activity {
private Button
private SPUtil
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLogin = (Button) findViewById(R.id.login);
mSpUtil = new SPUtil(MainActivity.this);
mLogin.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet get = new HttpGet(
"/user/login?"
+ "username=&"
+ "password=96ec92a549dd5a330112&"
+ "type=1&"
+ "userType=1&"
+ "appType=31&"
+ "agentID=0-maizuo&"
+ "loginType=1&"
+ "clientID=31&"
+ "channelID=31");
HttpResponse response = httpClient.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity);
System.out.println("result :" + result);
JSONObject jsonObject = new JSONObject(result);
String sessionKey = jsonObject.optString("sessionKey");
mSpUtil.putString("sessionKey", sessionKey);
} catch (Exception e) {
e.printStackTrace();
}).start();
findViewById(R.id.pay).setOnClickListener(new OnClickListener() {
public void onClick(View v) {
String spSessionKey = mSpUtil.getString("sessionKey", "");
if ("".equals(spSessionKey)) {
Toast.makeText(getApplicationContext(), "请先登录", 0).show();
3. 绘流程图说明sessionKey的使用
以确认支付协议为例
验证码登录/注册_简单流程
1、提交手机号 2、下发短信验证码 3、提交验证码
验证码后台过期逻辑
验证码登录/注册_流程描述
用户填写手机号,点击发送验证码,发送请求把手机号传到server
server调用短信平台的接口知道发送内容,发送对象,完成短信的发送
用户收到短信,得到验证码,填写验证码,发送请求把手机号,验证码上传到server
server判断我们的验证码是否正确,验证是否过期,根据不同的判断,返回不同的结果
share sdk 短信验证码理解
Android登录注册模块解决方案
原文链接:
几乎每个app都会有登录注册的功能,可以看看笔者开发的『南方周末新闻阅读器』,登录、手机注册、忘记密码这些入口,这些功能在app中要如何来实现呢?这个模块看似很简单,但要做好就需要考虑很多细节,比如对用户的输入的容错,操作的提示文案的设定,登录成功保存用户信息等等。
业务逻辑描述上一节的流程图已经很清晰的展现了登录注册的流程,这里继续用文字说明一下:
1. 点击进入个人中心或者需要用户登录状态的操作,先判断用户是否已经登录。
2. 如果已经登录,则继续后面的业务,否则,跳转到登录页面进行登录。
3. 如果已经有账号,则可以直接登录,或者可以直接选择第三方平台授权登录。
4. 如果未注册账号,则需要先进行账号注册,注册成功后再登录;也可以不注册账号,通过第三方平台授权进行登录。
5. 如果有账号,但忘记密码,则需要进行重置密码,否则直接登录。
具体实现登录可以使用账号登录,现在的app基本上都是手机号码登录,注册的时候也是一个手机对应一个账号,通过发送验证码进行验证;用户也可以选择第三方平台进行登录,一般会提供微信、QQ、新浪微博这样的主流社交平台进行授权登录,这里使用了友盟的SDK进行实现。
登录注册的解决方案,已经做成一个Demo,大家在实际开发的时候可以参考着根据自身的业务进行调整,但基本上不会差太多,第三方登录、验证码这个都可以选用第三方服务来实现
示例代码:LoginActivity.Java
package com.devilwwj.
import android.app.A
import android.content.I
import android.os.B
import android.text.TextU
import android.text.method.HideReturnsTransformationM
import android.text.method.PasswordTransformationM
import android.view.KeyE
import android.view.V
import android.view.inputmethod.EditorI
import android.widget.TextV
import android.widget.T
import com.devilwwj.loginandregister.global.AppC
import com.devilwwj.loginandregister.utils.LogU
import com.devilwwj.loginandregister.utils.ProgressDialogU
import com.devilwwj.loginandregister.utils.RegexU
import com.devilwwj.loginandregister.utils.ShareU
import com.devilwwj.loginandregister.utils.SpU
import com.devilwwj.loginandregister.utils.ToastU
import com.devilwwj.loginandregister.utils.U
import com.devilwwj.loginandregister.views.CleanEditT
import com.umeng.socialize.bean.SHARE_MEDIA;
import com.umeng.socialize.controller.UMServiceF
import com.umeng.socialize.controller.UMSocialS
import com.umeng.socialize.controller.listener.SocializeListeners.UMAuthL
import com.umeng.socialize.controller.listener.SocializeListeners.UMDataL
import com.umeng.socialize.exception.SocializeE
import java.util.M
import static android.view.View.OnClickL
* 登录界面
* Created by devilwwj on 16/1/24.
public class LoginActivity extends Activity implements OnClickListener {
private static final String TAG = "loginActivity";
private static final int REQUEST_CODE_TO_REGISTER = 0x001;
private CleanEditText accountE
private CleanEditText passwordE
private String accessT
private String expires_
private UMSocialService mController = UMServiceFactory
.getUMSocialService(AppConstants.DESCRIPTOR);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
initViews();
ShareUtils.configPlatforms(this);
* 初始化视图
private void initViews() {
accountEdit = (CleanEditText) this.findViewById(R.id.et_email_phone);
accountEdit.setImeOptions(EditorInfo.IME_ACTION_NEXT);
accountEdit.setTransformationMethod(HideReturnsTransformationMethod
.getInstance());
passwordEdit = (CleanEditText) this.findViewById(R.id.et_password);
passwordEdit.setImeOptions(EditorInfo.IME_ACTION_DONE);
passwordEdit.setImeOptions(EditorInfo.IME_ACTION_GO);
passwordEdit.setTransformationMethod(PasswordTransformationMethod
.getInstance());
passwordEdit.setOnEditorActionListener(new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_GO) {
clickLogin();
return false;
private void clickLogin() {
String account = accountEdit.getText().toString();
String password = passwordEdit.getText().toString();
if (checkInput(account, password)) {
* 检查输入
* password
public boolean checkInput(String account, String password) {
if (account == null || account.trim().equals("")) {
Toast.makeText(this, R.string.tip_account_empty, Toast.LENGTH_LONG)
if ( !RegexUtils.checkMobile(account)) {
Toast.makeText(this, R.string.tip_account_regex_not_right,
Toast.LENGTH_LONG).show();
} else if (password == null || password.trim().equals("")) {
Toast.makeText(this, R.string.tip_password_can_not_be_empty,
Toast.LENGTH_LONG).show();
return true;
return false;
public void onClick(View v) {
Intent intent = null;
switch (v.getId()) {
case R.id.iv_cancel:
case R.id.btn_login:
clickLogin();
case R.id.iv_wechat:
clickLoginWexin();
case R.id.iv_qq:
clickLoginQQ();
case R.id.iv_sina:
loginThirdPlatform(SHARE_MEDIA.SINA);
case R.id.tv_create_account:
enterRegister();
case R.id.tv_forget_password:
enterForgetPwd();
* 点击使用QQ快速登录
private void clickLoginQQ() {
if (!Utils.isQQClientAvailable(this)) {
ToastUtils.showShort(LoginActivity.this,
getString(R.string.no_install_qq));
loginThirdPlatform(SHARE_MEDIA.QZONE);
* 点击使用微信登录
private void clickLoginWexin() {
if (!Utils.isWeixinAvilible(this)) {
ToastUtils.showShort(LoginActivity.this,
getString(R.string.no_install_wechat));
loginThirdPlatform(SHARE_MEDIA.WEIXIN);
* 跳转到忘记密码
private void enterForgetPwd() {
Intent intent = new Intent(this, ForgetPasswordActivity.class);
startActivity(intent);
* 跳转到注册页面
private void enterRegister() {
Intent intent = new Intent(this, SignUpActivity.class);
startActivityForResult(intent, REQUEST_CODE_TO_REGISTER);
* 授权。如果授权成功,则获取用户信息
* platform
private void loginThirdPlatform(final SHARE_MEDIA platform) {
mController.doOauthVerify(LoginActivity.this, platform,
new UMAuthListener() {
public void onStart(SHARE_MEDIA platform) {
LogUtils.i(TAG, "onStart------"
+ Thread.currentThread().getId());
ProgressDialogUtils.getInstance().show(
LoginActivity.this,
getString(R.string.tip_begin_oauth));
public void onError(SocializeException e,
SHARE_MEDIA platform) {
LogUtils.i(TAG, "onError------"
+ Thread.currentThread().getId());
ToastUtils.showShort(LoginActivity.this,
getString(R.string.oauth_fail));
ProgressDialogUtils.getInstance().dismiss();
public void onComplete(Bundle value, SHARE_MEDIA platform) {
LogUtils.i(TAG, "onComplete------" + value.toString());
if (platform == SHARE_MEDIA.SINA) {
accessToken = value.getString("access_key");
accessToken = value.getString("access_token");
expires_in = value.getString("expires_in");
uid = value.getString(AppConstants.UID);
if (value != null && !TextUtils.isEmpty(uid)) {
getUserInfo(platform);
ToastUtils.showShort(LoginActivity.this,
getString(R.string.oauth_fail));
public void onCancel(SHARE_MEDIA platform) {
LogUtils.i(TAG, "onCancel------"
+ Thread.currentThread().getId());
ToastUtils.showShort(LoginActivity.this,
getString(R.string.oauth_cancle));
ProgressDialogUtils.getInstance().dismiss();
* 获取用户信息
* platform
private void getUserInfo(final SHARE_MEDIA platform) {
mController.getPlatformInfo(LoginActivity.this, platform,
new UMDataListener() {
public void onStart() {
LogUtils.i("getUserInfo", "onStart------");
ProgressDialogUtils.getInstance().dismiss();
ProgressDialogUtils.getInstance().show(
LoginActivity.this, "正在请求...");
public void onComplete(int status, Map info) {
String sns_id = "";
String sns_avatar = "";
String sns_loginname = "";
if (info != null && info.size() != 0) {
LogUtils.i("third login", info.toString());
if (platform == SHARE_MEDIA.SINA) {
sns = AppConstants.SINA;
if (info.get(AppConstants.UID) != null) {
sns_id = info.get(AppConstants.UID)
.toString();
if (info.get(AppConstants.PROFILE_IMAGE_URL) != null) {
sns_avatar = info
.get(AppConstants.PROFILE_IMAGE_URL)
.toString();
if (info.get(AppConstants.SCREEN_NAME) != null) {
sns_loginname = info.get(
AppConstants.SCREEN_NAME)
.toString();
} else if (platform == SHARE_MEDIA.QZONE) {
sns = AppConstants.QQ;
if (info.get(AppConstants.UID) == null) {
ToastUtils
.showShort(
LoginActivity.this,
getString(R.string.oauth_fail));
sns_id = info.get(AppConstants.UID)
.toString();
sns_avatar = info.get(
AppConstants.PROFILE_IMAGE_URL)
.toString();
sns_loginname = info.get(
AppConstants.SCREEN_NAME)
.toString();
} else if (platform == SHARE_MEDIA.WEIXIN) {
sns = AppConstants.WECHAT;
sns_id = info.get(AppConstants.OPENID)
.toString();
sns_avatar = info.get(
AppConstants.HEADIMG_URL)
.toString();
sns_loginname = info.get(
AppConstants.NICKNAME).toString();
SpUtils.putBoolean(LoginActivity.this,
AppConstants.THIRD_LOGIN, true);
LogUtils.e("info", sns + "," + sns_id + ","
+ sns_loginname);
} catch (Exception e) {
e.printStackTrace();
示例代码:SignUpActivity.java
package com.devilwwj.
import android.app.A
import android.os.B
import android.text.TextU
import android.util.L
import android.view.KeyE
import android.view.V
import android.view.View.OnClickL
import android.view.inputmethod.EditorI
import android.widget.B
import android.widget.TextV
import android.widget.TextView.OnEditorActionL
import com.devilwwj.loginandregister.utils.RegexU
import com.devilwwj.loginandregister.utils.ToastU
import com.devilwwj.loginandregister.utils.VerifyCodeM
import com.devilwwj.loginandregister.views.CleanEditT
* 注册界面
* 功能描述:一般会使用手机登录,通过获取手机验证码,跟服务器交互完成注册
* Created by devilwwj on 16/1/24.
public class SignUpActivity extends Activity implements OnClickListener{
private static final String TAG = "SignupActivity";
private CleanEditText phoneE
private CleanEditText passwordE
private CleanEditText verifyCodeE
private Button getVerifiCodeB
private VerifyCodeManager codeM
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_signup);
initViews();
codeManager = new VerifyCodeManager(this, phoneEdit, getVerifiCodeButton);
* 通用findViewById,减少重复的类型转换
@SuppressWarnings("unchecked")
public final
E getView(int id) {
return (E) findViewById(id);
} catch (ClassCastException ex) {
Log.e(TAG, "Could not cast View to concrete class.", ex);
private void initViews() {
getVerifiCodeButton = getView(R.id.btn_send_verifi_code);
getVerifiCodeButton.setOnClickListener(this);
phoneEdit = getView(R.id.et_phone);
phoneEdit.setImeOptions(EditorInfo.IME_ACTION_NEXT);
verifyCodeEdit = getView(R.id.et_verifiCode);
verifyCodeEdit.setImeOptions(EditorInfo.IME_ACTION_NEXT);
passwordEdit = getView(R.id.et_password);
passwordEdit.setImeOptions(EditorInfo.IME_ACTION_DONE);
passwordEdit.setImeOptions(EditorInfo.IME_ACTION_GO);
passwordEdit.setOnEditorActionListener(new OnEditorActionListener() {
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_DONE
|| actionId == EditorInfo.IME_ACTION_GO) {
return false;
private void commit() {
String phone = phoneEdit.getText().toString().trim();
String password = passwordEdit.getText().toString().trim();
String code = verifyCodeEdit.getText().toString().trim();
if (checkInput(phone, password, code)) {
private boolean checkInput(String phone, String password, String code) {
if (TextUtils.isEmpty(phone)) {
ToastUtils.showShort(this, R.string.tip_phone_can_not_be_empty);
if (!RegexUtils.checkMobile(phone)) {
ToastUtils.showShort(this, R.string.tip_phone_regex_not_right);
} else if (TextUtils.isEmpty(code)) {
ToastUtils.showShort(this, R.string.tip_please_input_code);
} else if (password.length() & 6 || password.length() & 32
|| TextUtils.isEmpty(password)) {
ToastUtils.showShort(this,
R.string.tip_please_input_6_32_password);
return true;
return false;
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_send_verifi_code:
codeManager.getVerifyCode(VerifyCodeManager.REGISTER);
集成步骤(移动应用接入流程)
1.注册成为开发者,登录
2.点击申请加入,创建应用–&会分配appId,appKey
APP KEY:PzMWIM4GYZvxGRjd
3.完善信息
4.下载demo.运行看效果
5.集成开发
webview方式(老方式):授权的时候是跳到了一个webview上去授权
sso方式:如果手机里面装了qq.那就是单点登录的形式.如果没有装qq,就是跳到webview这种老方式;
sso:单点登录
集成步骤_官方
1.注册成为开发者
2.创建应用
3.完善应用信息
4.提交上线申请
5.通过审核上线
通过广播的方式拿到access_token
1、点击了发起授权请求的按钮,使用WebView加载登陆网页,也是oauth2.0授权步骤中的第一步,其实就是去打开了一个授权的网页
通过Get方式传递三个参数: client_id(应用的appkey),redirect_uri(回调地址),display(展示方式,手机设备为mobile)
2、用户授权完成之后,回调到回调页,同时传递code(授权码)
在wap页点击登陆按钮后, 会有一个回调地址, 可以在WebViewClient的shouldOverrideUrlLoading方法中捕获。
用户点击登录后, 是一个302跳转, 跳转地址就是申请appkey时填写的Redirect URL, 登录成功后这个地址后面带有code参数. 如果是移动端的话, 这个地址不需要有实体的页面
3、拿着授权的code请求access_token
请求方式:post
请求参数的形式:key-value
请求具体参数
client_id=&client_secret=2b6c964b071e2ecc28c01&grant_type=authorization_code&code=ceb7401477ff&redirect_uri=http%3A%2F%
"access_token": "yk3e3x6rXZ3gOj5J35b6d",
"expires_in": ,
"time_left": 22868,
"uid": "",
"refresh_token": "f244fe3x6rXZ3gOj5Jf1bb7"
获取用户信息
access_token加密
access_token的保存,有关加密算法的相关知识,
this.PASSWORD = "com.sina.vdisk.security.password.d7afff47ae5"
+ IMEI + appKeyPair.key + appKeyPair.secret
有别于sim卡的序列号,可以作为手机的唯一标识,类似我们pc的机器码
imei:设备的唯一标识
imsi:移动sim卡的唯一标识
买手机:3码合一 手机序列号 电池序列号 手机包装序列号
统计apk的安装量:启动应用程序的时候,把手机的imei号上传到服务器
TelephonyManager telephonyManager = (TelephonyManager) ctx .getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.getDeviceId();
只是加大了被破解的难度
des加密–&密码唯一化,复杂化(但是还是不安全)
this.PASSWORD = "com.sina.vdisk.security.password.d7afff47ae5" + IMEI + appKeyPair.key + appKeyPair.secret
秘钥放到so库里面,通过jni调用-&密码放到so库里面,这个时候,加到了获取秘钥的难度。(同样,反编译apk,可以拿到so库,然后可以调用本地方法获取到密码)
public native String getDataFromC();
public static String getKey(){
return getInstance().getDataFromC();
#include &jni.h&
jstring Java_com_example_level3encrypt_util_LanguageTransform_getDataFromC(JNIEnv* env,
jobject obj) {
char* cstr = "myKey0510YUY1234";
jstring jstr = (**env).NewStringUTF(env, cstr);
混淆(也是可以拿到,混淆的时候。我们的字符串是不会进行混淆的。只是混淆我们方法名,以及变量名)
第三方登录
用qq号/微信号/微博号去登录自己的应用
核心:就是拿到我们access_token
实际开发第三方登录的协议:&xxx&type=x,token就是我们授权之后返回的access_token,type是为了区分不同登录渠道
第三方登录协议
access_token是啥,干嘛用?
形象解释:申请调兵–&皇帝同意–&兵符–&开始调兵
拿到用户在第三方平台的唯一的标识;
获取用户的nickname,头像,邮箱等其他信息;
实际开发3大步
app做的事情,实际开发,我们能把我这里的几个步骤,就可以完成开发工作
1.发起授权请求,让用户授权(输入账号密码)
2.处理授权结果,拿到access_token
3.调用第三方登录协议(自己公司定义),传递access_token到app的server,后续逻辑交给server
server拿到access_token做了什么
静默注册新用户或者返回已有用户用户信息
1.使用access_token拿到用户在第三方平台的唯一ID;
判断第三方平台的唯一ID是否存在我们的用户信息表中;
存在:(之前使用qq号登陆过自己的系统)返回当前用户的用户信息
不存在:(用户还没有使用过此qq登陆过我们的系统)
2. 调用相关的接口,拿到nickname,邮箱,头像(需要什么拿取什么);
qq登录_sso形式
sso(单点登录)方式:如果手机里面装了qq,那就是单点登录的形式,如果没有装qq,就是跳到webview这种老方式
什么是开发平台
开放平台(Open Platform) 在软件业和网络中,开放平台是指软件系统通过公开其应用程序编程接口(API)或函数(function)来使外部的程序可以增加该软件系统的功能或使用该软件系统的资源,而不需要更改该软件系统的源代码
开发平台的作用:提供功能,提供资源
OAuth是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用
oauth产生背景
传统互联网时代,各个网站和服务之间是封闭的,数据无法进行交互。随着互联网技术的迅猛发展、系统之间的相互协作日益增多,开放与共享数据的需求不断增加,互联网服务之间的整合已经成为必然趋势,一个通过自身开放平台来实现数据互通甚至用户共享的时代已经来临。把网站的服务封装成一系列计算机易识别的数据接口开放出去,供第三方开发者使用,这种行为就叫做Open API,提供开放API 的平台本身就被称为开放平台。
2007 年5 月份,Facebook 宣布改版,最早提出了开放平台的概念,正式从一个社交网站向一个社交应用平台转型。至此,开放平台发展迅速,成为互联网发展的趋势,也是一种革命性的发展模式
国内外已经有很多的公司开发了自己的开放平台,Facebook、Twitter、腾讯、新浪微博、人人网,它们用户规模大、技术实力强,为第三方提供了一整套自成体系、纷繁复杂的“开放API”。通过开放平台,网站不仅能提供对Web 网页的简单访问,还可以进行复杂的数据交互,允许第三方开发者利用其资源开发复杂的应用,既丰富自身网站应用,为用户提供更好的服务,逐步建立起一个服务完备的网络社会,也为第三方连接的网站带来更多的用户。开放平台迅速成为互联网发展的趋势。
开放平台的核心问题在于用户验证和授权。对于服务提供商来说,一般不会希望第三方直接使用用户名和密码来验证用户身份,除非双方具有很强的信任关系。OAuth 协议正是为了解决服务整合时“验证和授权”这一根本问题而产生的,具有同样认证功能的协议还有Openid。
如果一个用户拥有两项服务:一项服务是图片在线存储服务A,另一个是图片在线打印服务B。如下图所示。由于服务A与服务B是由两家不同的服务提供商提供的,所以用户在这两家服务提供商的网站上各自注册了两个用户,假设这两个用户名各不相同,密码也各不相同。当用户要使用服务B打印存储在服务A上的图片时,用户该如何处理?
方法一:用户可能先将待打印的图片从服务A上下载下来并上传到服务B上打印,这种方式安全但处理比较繁琐,效率低下
方法二:用户将在服务A上注册的用户名与密码提供给服务B,服务B使用用户的帐号再去服务A处下载待打印的图片,这种方式效率是提高了,但是安全性大大降低了,服务B可以使用用户的用户名与密码去服务A上查看甚至篡改用户的资源。很多公司和个人都尝试解决这类问题,包括Google、Yahoo、Microsoft,这也促使OAUTH项目组的产生。OAuth是由Blaine Cook、Chris  Messina、Larry Halff 及David Recordon共同发起的,目的在于为API访问授权提供一个开放的标准。
什么是OAuth
OAuth是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth允许用户授权第三方网站访问他们存储在另外的服务提供者上的信息。
简单:不管是OAUTH服务提供者还是应用开发者,都很容易于理解与使用
安全:没有涉及到用户密钥等信息,更安全更灵活
开放:任何服务提供商都可以实现OAUTH,任何软 件开发商都可以使用OAUTH
OAuth的核心工作流程
OAuth 为客户端提供了一种代表资源拥有者访问受保护资源的方法。在客户端访问受保护资源之前,它必须先从资源拥有者获取授权(访问许可),然后用访问许可交换访问令牌(Access Token,包含许可的作用域、持续时间和其它属性等信息)。客户端通过向资源服务器出示访问令牌来访问受保护资源。
OAuth1.0主要流程是四步骤:
1、获取未授权的Request Token
2、请求用户授权Request Token
3、使用授权后的Request Token换取Access Token
4、使用 Access Token 访问或修改受保护资源。
1.0的缺陷:
1、oauth1.0对手机客户端,移动设备等非server第三方的支持不好。是因为oauth1.0将多种流程合并成了一种,而事实证明,这种合并的流程体验性非常差。
2、oauth1.0的三步认证过程比较繁琐和复杂,对第三方开发者增加了极大的开发难度。
3、oauth1.0的加密需求过于复杂,第三方开发者使用oauth之前需要花费精力先实现oauth1.0的加密算法。
4、oauth1.0生成的access_token要求是永久有效的,这导致的问题是网站的安全性和破坏网站的架构。
oauth2.0针对1.0的各种问题提供了解决方法:
1、 oauth2.0提出了多种流程,各个客户端按照实际情况选择不同的流程来获取access_token。这样就解决了对移动设备等第三方的支持,也解决了拓展性的问题
2 、oauth2.0删除了繁琐的加密算法。利用了https传输对认证的安全性进行了保证
3、 oauth2.0的认证流程一般只有2步,对开发者来说,减轻了负担
4 、oauth2.0提出了access_token的更新方案,获取access_token的同时也获取refresh_token, access_token是有过期时间的,refresh_token的过期时间较长,这样能随时使用refresh_token对access_token进行更新
oauth2.0授权流程
用户打开客户端以后,客户端要求用户给予授权。
用户同意给予客户端授权。
客户端使用上一步获得的授权,向认证服务器(比如qq登录,那就是腾讯)申请令牌。
认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
客户端使用令牌,向资源服务器申请获取资源。
资源服务器确认令牌无误,同意向客户端开放资源
授权流程图
Oauth2.0根据授权方式和客户端状态的不同对应不一样的实现方式。
Webserver方式
适用场景:Web Server子态适用于有能力与终端用户的user-agent(通常是浏览器)交互并能够从授权服务器接收(通过重定向)请求(即有能力担当HTTP服务器的角色)的客户端。
userAgent方式
适用场景:Web Server子态适用于有能力与终端用户的user-agent(通常是浏览器)交互并能够从授权服务器接收(通过重定向)请求(即有能力担当HTTP服务器的角色)的客户端 ,典型的例子是用诸如JavaScript语言编写并运行在浏览器的程序。这些客户端不能保存客户端私有证书,并且客户端的验证基于user-agent的同源策略。
说明:在其它子态中,客户端对于终端用户的授权和访问令牌使用分开的不同请求来完成,而与之不同的是,在user-agent子态中,客户端以HTTP重定向的方式在终端用户授权请求的结果中获取到访问令牌。客户端请求授权服务器将user-agent重定向到另一个web服务器或user-agent能访问到的本地资源,而且user-agent有能力从响应信息中提取出访问令牌并传给客户端。
原生程序方式
适用场景:原生程序方式适用于作为原生代码运行在终端用户计算机或移动设备上的客户端。这些客户端通常有能力与终端用户的user-agent交互(或嵌入user-agent)。
基于不同的需求和期望的终端用户体验,原生程序客户端可以有三种方式实现:
外部启用一个user-agent;
内嵌一个user-agent;
直接要求用户输入用户名和密码。
外部浏览器可能会提高完成效率,因为终端用户可能已经登录过了而不需要重新进行身份验证。
嵌入的user-agent通常能提供更好的用户流程,因为它不必切换上下文并打开新窗口。
但嵌入的user-agent对安全提出了挑战,因为用户在一个无法辨别的窗口之中进行身份验证,而不像很多user-agent那样能提供可视化的保护。
第三种方式,实现起来最简单,但它将终端用户的密码直接交给了第三方客户端,而客户端不得不用明文存储密码,所以它要求客户端和认证服务方有很强的信任关系。
OAuth2.0引入刷新令牌的方式重新获取访问令牌。访问令牌的生命周期通常比资源拥有者授予的要短一些。当分发一个访问令牌时,授权服务器可以同时传回一个刷新令牌,在当前访问令牌超时后,客户端可以用这个刷新令牌重新获取一个访问令牌。当请求新的访问令牌时,刷新令牌担当起访问许可的角色。使用刷新令牌,不再需要再次与资源拥有者交互,也不需要存储原始的访问许可来获得访问令牌和刷新令牌。
注:可以使用刷新令牌也可以直接使用客户端的私有证书
oauth2.0涉及的角色
Third-party application:第三方应用程序,本文中又称”客户端”(client),即上一节例子中的”云冲印”
HTTP service:HTTP服务提供商,本文中简称”服务提供商”,即上一节例子中的Google
Resource Owner:资源所有者,本文中又称”用户”(user)。
User Agent:用户代理,本文中就是指浏览器
Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器
Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器
说明:本文提出的OAuth使用方式是基于2.0协议,只对OAuth2.0 最核心的工作流程进行设计,即获取Access Token 的过程。
服务器端的数据存储方案
OAuth2.0 机制的核心工作流程,大部分信息,如APP 信息、用户和APP 之间的授权关系、Access Token等,需要永久性存储,存储在数据库中。一些不需要永久存储的信息,如临时授权码,只使用一次,而且,在很短的时间内就要使其失效,就没有必要存储在数据库之中,可存储在譬如memcached 等缓存系统中,即提高了系统的处理速度,又减少了数据库压力
数据库表结构设计
客户端要先注册一个应用,获取该应用的APPID和APPSECRET,应用的详细信息存储在数据表中,如下图所示:
用户授权信息表用来保存授权成功的访问令牌。
OAuth和Openid的比较
Openid是什么
OpenID 是一个以用户为中心的数字身份识别框架。
OpenID 的创建基于这样一个概念:我们可以通过 URI (又叫 URL 或网站地址)来认证一个网站的唯一身份,同理,我们也可以通过这种方式来作为用户的身份认证。由于URI 是整个网络世界的核心,它为基于URI的用户身份认证提供了广泛的、坚实的基础。
OAuth和Openid同为认证机制,但它们的侧重点不一样。
OAuth关注的授权,即:“用户能做什么” ;
而OpenID关注的是证明,即:“用户是谁”。
下面就分别来举例说明两者的功能。
用户希望访问其在的账户
< 提示用户输入他/她/它的OpenID
用户给出了他的OpenID,比如说””
< 跳转到了用户的OpenID提供商“”
用户在””(OpenID provider)提示的界面上输入用户名密码登录
“” (OpenID provider) 问用户是否要登录到
用户同意后,”” (OpenID provider) 跳转回
< 允许用户访问其资源
用户在使用时希望从导入他的联系人
< (在OAuth的黑话里面叫“Consumer”)把用户送往 (黑话是“Service Provider”)
用户在 登录
<问用户是不是希望授权访问他在的联系人
< 把用户送回
< 从拿到联系人
< 告诉用户导入成功
本文已收录于以下专栏:
相关文章推荐
为了让网站更快接入,腾讯提供了JS SDK的接入方案,具体点击这里或者点击这里进行查看。
&#160; &#160; 不过也可以自己写代码,当然你需要了解QQ的OAuth2.0的处理流程(Web Server Flow)...
1.用户名密码加密后存在客户端、cookie
2.登录成功后,服务端返回token,存在客户端或cookie,每次请求根据token获取状态。token保存在session或者redis
3.登录成功...
OAuth学习笔记
October 8, 2012
/archives/oauth-qq1.0-developer.html
OAuth1.0认证中URL的调...
关注公众号
在公众号里回复“”秘密“”两个字
http://task.csdn.net/m/task/home?task_id=398 领取奖励
提示:根据公众号里的自动回复,完成...
观察者模式在实际项目中使用的也是非常频繁的,它最常用的地方是GUI系统、订阅——发布系统等。因为这个模式的一个重要作用就是解耦,使得它们之间的依赖性更小,甚至做到毫无依赖。以GUI系统来说,应用的UI...
LinearLayout xmlns:android=&/apk/res/android&
xmlns:app=&http://schema...
JavaEE 6及以上版本的web.xml问题?MyEclipse JavaEE 6版本开始web.xml突然消失不见?没这回事,只是不太必须而已,有需要的项目可以自行进行添加或在创建项目的时候点击n...
注:本文的高斯模糊只能显示,如果想要保存模糊后的图片,请参考另一篇文章:http://blog.csdn.net/fan7983377/article/details/
他的最新文章
讲师:吴岸城
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 app session 验证登录 的文章

 

随机推荐