使用Mozilla Persona认证用户的指南

到目前为止,只有Twitter和Facebook身份验证,我决定将Mozilla Persona添加到我最新项目( 计算机 ,计算机生成的音乐)的列表中。 为什么?

  • 我喜欢尝试新事物
  • 存储密码是一个艰巨的过程,尽管我知道该怎么做,甚至大部分代码都写在另一个项目中,但我认为我不应该为每个需要密码认证的站点做出贡献
  • Mozilla是一个开放的基金会,迄今为止已经产生了许多出色的产品。 Persona实现了BrowserID协议,将来可能会在Firefox以外的其他浏览器中本地支持(目前,您需要包含.js文件)
  • 第三方身份验证已经尝试了很多次,这是一件很了不起的事情,但是由于一些原因,它并不是主流。 有所不同,《女神异闻录》可能会成功地变得更受欢迎。
  • Mozilla的解释很有意义

因此,我从“快速设置”指南开始。 看起来真的很容易。 比OpenID或OAuth身份验证容易得多–您无需在任何地方注册任何内容,不需要第三方库来在服务器上处理验证,并且您无需学习复杂的身份验证流程,因为该流程很简单:

  1. 用户单击登录按钮
  2. 出现一个弹出窗口
  3. 如果未通过Persona认证,则提示用户注册
  4. 如果通过Persona进行了身份验证,则提示用户批准对该站点的身份验证
  5. 弹出窗口关闭,页面重定向/刷新-用户现在已登录

当然,它不是那么简单,但是有一些需要注意的地方在本教程中没有提到。 因此,让我们一步一步地遵循官方教程,并且我将在每一点上进行扩展(使用的服务器端语言是Java,但它很简单,您可以使用任何语言来完成)

1.包括.js文件-简单。 建议从Mozilla服务器获取js文件,而不是将其存储在本地,因为它可能会更改(例如,为了修复错误)。 如果将js文件合并到一个文件中(为了更快地加载页面)可能会比较棘手,但是可能您的机制允许加载远程js文件。

2.登录和退出按钮。 这看起来也很容易。 最好有条件地添加注销处理程序–仅当用户已使用Persona登录时(而不是您的站点支持的其他身份验证方法)

3.侦听身份验证事件。 建议将侦听事件放在所有页面上(例如,包含在标题模板中)。 但是这里有个问题。 如果您的用户已经在Persona中进行了身份验证,但是他的会话在您的站点上已过期,则脚本将自动登录该用户。 这将需要在页面加载后的几秒钟内重新加载页面。 这并不一定是您或用户想要的-例如,在我的情况下,这可能意味着他们刚刚播放的曲目由于页面刷新而中断。 当然可以使用AJAX来完成,但是当UI中的某些内容没有明显原因发生更改时,肯定会造成混乱。 下面,我将显示针对此问题的修复程序。 另外,注销侦听器可能并不是到处都需要的-据我了解,万一您注销了Persona,它将自动注销用户。 这可能不是您想要的,例如,用户可能希望保留一些打开的选项卡,其中某些文件在注销时无法访问。

4.验证服务器上的断言。 在这里,您可能需要第3方库来调用验证端点并解析json结果,但是这些是您可能已经包含的非常标准的库。

现在,如何解决自动身份验证的问题? 声明一个新变量userRequestedAuthentication ,该变量保存身份验证是由用户显式发起还是由用户自动发起。 在登录按钮中,单击处理程序,将该变量设置为true 。 这是js代码的样子(顺便说一句,我认为可以将代码放在document.ready()中,而不是直接放在script标记中。假设您以后需要在处理程序方法中使用一些DOM资源,页面已完全加载。另一方面,这可能会减慢该过程的速度)。 请注意,您可以在所有页面上包括一个空的onlogin处理程序,并且仅在身份验证页面上具有完整的处理程序。 但是,鉴于登录按钮位于主页上,或带有javascript模态窗口,因此可以在任何地方/在多个页面上都可以使用。

<script type='text/javascript'>
    var loggedInUser = ${context.user != null ? ''' + context.user.email + ''' : 'null'};
    var userRequestedAuthentication = false;
    navigator.id.watch({
        loggedInUser : loggedInUser,
        onlogin : function(assertion) {
            $.ajax({
                type : 'POST',
                url : '${root}/persona/auth',
                data : {assertion : assertion, userRequestedAuthentication : userRequestedAuthentication},
                success : function(data) {
                    if (data != '') {
                        window.location.href = '${root}' + data;
                    }
                },
                error : function(xhr, status, err) {
                    alert('Authentication failure: ' + err);
                }
            });
        },
        onlogout : function() {
            window.locaiton.open('${root}/logout');
        }
    });
</script>

如您所见,参数被传递到服务器端代码。 那里发生了什么?

@RequestMapping('/persona/auth')
@ResponseBody
public String authenticateWithPersona(@RequestParam String assertion,
        @RequestParam boolean userRequestedAuthentication, HttpServletRequest request, Model model)
        throws IOException {
    if (context.getUser() != null) {
        return '';
    }
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.add('assertion', assertion);
    params.add('audience', request.getScheme() + '://' + request.getServerName() + ':' + (request.getServerPort() == 80 ? '' : request.getServerPort()));
    PersonaVerificationResponse response = restTemplate.postForObject('https://verifier.login.persona.org/verify', params, PersonaVerificationResponse.class);
    if (response.getStatus().equals('okay')) {
        User user = userService.getUserByEmail(response.getEmail());
        if (user == null && userRequestedAuthentication) {
            return '/signup?email=' + response.getEmail();
        } else if (user != null){
            if (userRequestedAuthentication || user.isLoginAutomatically()) {
                context.setUser(user);
                return '/';
            } else {
                return '';
            }
        } else {
            return ''; //in case this is not a user-requested operation, do nothing
        }
    } else {
        logger.warn('Persona authentication failed due to reason: ' + response.getReason());
        throw new IllegalStateException('Authentication failed');
    }
}

逻辑看起来比您希望的更复杂,但是让我解释一下:

  • 正如您在javascript代码中看到的那样,空字符串表示“不执行任何操作”。 如果返回任何其他内容,则javascript将打开该页面。 如果不使用spring-mvc,则无需将其从方法中返回@ResponseBody字符串,而是将其写入响应输出流中(或用php术语–将其回显)。
  • 首先,您检查系统中是否已经有经过身份验证的用户。 如果有,则什么也不做。 我不确定是否存在Persona在已通过身份验证的用户上调用“ onlogin”的情况,但是如果您使用其他身份验证选项,Persona不会知道您的用户已经使用Twitter登录。
  • 然后,您调用验证URL并将结果解析为JSON。 我已经使用过RestTemplate ,但是任何东西都可以使用RestTemplate ,URLConnection。 对于JSON解析,spring在后台使用了Jackson。 您只需要编写一个值对象,即可保存Persona可能返回的所有属性。 到目前为止,我只包括:状态,电子邮件和原因(杰克逊详细信息:ignoreUnknown = true,spring-mvc详细信息:您需要将FormHttpMessageConverter设置为RestTemplate )。 重要的是,“受众”参数必须是用户当前所在的域。 无论是否使用www,它都会有所不同,因此请对其进行重构,而不是对其进行硬编码或从属性中加载它。 即使您从www重定向到no-www(反之亦然),您仍应为测试而动态获取该URL –测试环境的URL与生产环境的URL不同。
  • 如果Persona身份验证为“可以”,则您尝试在数据库中查找具有该电子邮件的用户。
  • 如果没有这样的用户,并且已手动触发了身份验证操作,则将用户发送到注册页面并提供电子邮件作为参数(您也可以在http会话中进行设置,以使用户无法对其进行修改)。 然后,注册页面会询问其他详细信息-名称,用户名,出生日期或您认为合适的任何信息(但请尽量减少-最好仅是全名)。 如果仅需要电子邮件地址,而无需其他任何内容,则可以跳过注册页面并强制注册用户。 注册完成后,您登录用户。 请注意,如果您已将电子邮件存储在会话中(即用户无法从注册页面进行修改),则可以跳过确认电子邮件-Persona已确认该电子邮件
  • 如果您的数据库中有一个使用该电子邮件的用户,请检查该用户是否已请求该操作,或者是否已(通过注册页面中的复选框)指示他要自动登录。考虑–应该询问用户,还是应该始终将其设置为true或false? 我已经添加了复选框。 如果应该登录,则将用户设置在会话中,然后重定向到home(或上一页,或“用户home”的任何页面)(“ context”是会话范围的Bean。您可以将其替换为session.setAttribute('user', user) )。 如果身份验证尝试是自动的,但用户不希望这样做,则不执行任何操作。 最后一个“其他”是针对用户在您的站点上没有帐户并且触发了自动身份验证的情况–在这种情况下不执行任何操作,否则最终将无休止地重定向到注册页面
  • 如果验证失败,请务必记录原因-然后您可以通过查看日志来检查一切是否正常

使用电子邮件作为唯一标识符的一个很酷的副作用是(如果使数据库列成为唯一 ),如果稍后将Persona添加到您的网站,即使用户以其他方式注册(例如,facebook或常规注册),用户也可以登录。 因此,他们可以将密码设置为长而难以记住的密码,并仅使用Persona继续登录。

我从实现中省略的细节很简单:注册页面只是收集字段并将其提交给/ completeRegistration处理程序,该处理程序将新用户存储在数据库中。 / logout URL仅清除会话(如果存储了cookie,则清除cookie)。 顺便说一句,如果启用了自动登录,并且Persona是您唯一的身份验证方法,则可能不需要存储cookie来保持会话过期后仍保持用户登录状态。

总体而言,即使我提出了要点,实现也仍然很简单。 女神异闻录看起来很棒,我希望很快能在更多站点上看到它。

参考: Bozho的技术博客博客上的JCG合作伙伴 Bozhidar Bozhanov的 Mozilla Persona身份验证用户指南

翻译自: https://www.javacodegeeks.com/2012/12/a-guide-to-authenticating-users-with-mozilla-persona.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值