第一步:修改liferay-hook.xml

<?xml version="1.0"?>
<!DOCTYPE hook PUBLIC "-//Liferay//DTD Hook 6.2.0//EN" "http://www.liferay.com/dtd/liferay-hook_6_2_0.dtd"> <hook>
<portal-properties>portal.properties</portal-properties>
</hook>

如果是liferay7则不需要这一步,只需要注解:

@Component(
immediate = true, property = {"key=auth.pipeline.pre"},
service = Authenticator.class
)

第二步:配置认证属性portal.properties

auth.pipeline.pre=com.proliferay.YourAuthenticator

配置auth.pipeline.post 还将进行密码检查,liferay的内部机制是2级检查,一级是身份认证,二级是密码检查,实际上可以通过SKIP_LIFERAY_CHECK来统一处理

第三步:开发定制的认证类

import java.util.Map;
import com.liferay.portal.security.auth.AuthException;
import com.liferay.portal.security.auth.Authenticator; public class YourAuthenticator implements Authenticator { @Override
public int authenticateByEmailAddress(long companyId, String emailAddress,
String password, Map<String, String[]> headerMap,
Map<String, String[]> parameterMap) throws AuthException { /**
* 这里是认证的逻辑
*/ return SKIP_LIFERAY_CHECK;
} @Override
public int authenticateByScreenName(long companyId, String screenName,
String password, Map<String, String[]> headerMap,
Map<String, String[]> parameterMap) throws AuthException { return DNE;
} @Override
public int authenticateByUserId(long companyId, long userId,
String password, Map<String, String[]> headerMap,
Map<String, String[]> parameterMap) throws AuthException {
return DNE;
} }

常数定义:

  • public static final int DNE = 0; //用户不存在
  • public static final int FAILURE = -1;//认证失败
  • public static final int SKIP_LIFERAY_CHECK = 2;
  • public static final int SUCCESS = 1;

要注意SKIP_LIFERAY_CHECK和SUCCESS的区别,通过SKIP_LIFERAY_CHECK来统一处理身份认证,跳过门户的密码检查,如果返回SUCCESS,则必须配合auth.pipeline.post来进行密码检查。
通过图来说明:

一个具体的例子:

集成Apache Shiro的认证登陆

import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.security.auth.AuthException;
import com.liferay.portal.kernel.security.auth.Authenticator; import java.util.Map; import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory; import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component; @Component(
immediate = true, property = {"key=auth.pipeline.pre"},
service = Authenticator.class
)
public class ShiroAuthenticatorPre implements Authenticator { @Activate
public void activate() {
Factory<SecurityManager> factory = new IniSecurityManagerFactory(
"classpath:userauth.ini"); //shiro配置文件 SecurityUtils.setSecurityManager(factory.getInstance()); if (_log.isInfoEnabled()) {
_log.info("activate");
}
} @Override
public int authenticateByEmailAddress(
long companyId, String emailAddress, String password,
Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
throws AuthException { if (_log.isInfoEnabled()) {
_log.info("authenticateByEmailAddress");
} UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
emailAddress, password); Subject currentUser = SecurityUtils.getSubject(); try {
//shiro的代理登陆
currentUser.login(usernamePasswordToken); boolean authenticated = currentUser.isAuthenticated(); if (authenticated) {
if (_log.isInfoEnabled()) {
_log.info("authenticated");
} return SKIP_LIFERAY_CHECK; //认证通过
}
else {
return FAILURE;
}
}
catch (AuthenticationException ae) {
_log.error(ae.getMessage(), ae);
throw new AuthException(ae.getMessage(), ae);
}
} @Override
public int authenticateByScreenName(
long companyId, String screenName, String password,
Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
throws AuthException { if (_log.isInfoEnabled()) {
_log.info("authenticateByScreenName - not implemented ");
} return SUCCESS;
} @Override
public int authenticateByUserId(
long companyId, long userId, String password,
Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
throws AuthException { if (_log.isInfoEnabled()) {
_log.info("authenticateByScreenName - not implemented ");
} return SUCCESS;
} private static final Log _log = LogFactoryUtil.getLog(
ShiroAuthenticatorPre.class); }

apache shiro框架结构

apache shiro是一套非常著名的安全框架,提供了认证、授权、加密和会话管理功能
了解更多apache shiro的知识:http://www.infoq.com/cn/articles/apache-shiro

Authentication的Token令牌机制
了解更多Authentication Token:https://web.liferay.com/zh/community/wiki/-/wiki/Main/Authentication+Token

Token令牌是为了避免CSRF跨站伪造。
了解更多CSRF:https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)

定义认证失败的扩展处理

import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.security.auth.AuthException;
import com.liferay.portal.kernel.security.auth.AuthFailure;
import com.liferay.portal.kernel.service.UserLocalServiceUtil;
import java.util.Map;
import org.osgi.service.component.annotations.Component; @Component(
immediate = true, property = {"key=auth.failure"},
service = AuthFailure.class
)
public class LogAuthFailure implements AuthFailure { @Override
public void onFailureByEmailAddress(
long companyId, String emailAddress,
Map<String, String[]> headerMap, Map<String, String[]> parameterMap)
throws AuthException { try {
User user = UserLocalServiceUtil.getUserByEmailAddress(
companyId, emailAddress); int failures = user.getFailedLoginAttempts(); if (_log.isInfoEnabled()) {
_log.info(
"onFailureByEmailAddress: " + emailAddress +
" has failed to login " + failures + " times");
}
}
catch (PortalException pe) {
}
} @Override
public void onFailureByScreenName(
long companyId, String screenName, Map<String, String[]> headerMap,
Map<String, String[]> parameterMap)
throws AuthException { try {
User user = UserLocalServiceUtil.getUserByScreenName(
companyId, screenName); int failures = user.getFailedLoginAttempts(); if (_log.isInfoEnabled()) {
_log.info(
"onFailureByScreenName: " + screenName +
" has failed to login " + failures + " times");
}
}
catch (PortalException pe) {
}
} @Override
public void onFailureByUserId(
long companyId, long userId, Map<String, String[]> headerMap,
Map<String, String[]> parameterMap)
throws AuthException { try {
User user = UserLocalServiceUtil.getUserById(userId); int failures = user.getFailedLoginAttempts(); if (_log.isInfoEnabled()) {
_log.info(
"onFailureByUserId: userId " + userId +
" has failed to login " + failures + " times");
}
}
catch (PortalException pe) {
}
} private static final Log _log = LogFactoryUtil.getLog(LogAuthFailure.class); }

优秀的平台必然松耦合、易扩展。

Liferay7 BPM门户开发之32: 实现自定义认证登陆(定制Authentication Hook)的更多相关文章

  1. Liferay7 BPM门户开发之37: Liferay7下的OSGi Hook集成开发

    hook开发是Liferay客制扩展的一种方式,比插件灵活,即可以扩展liferay门户,也能对原有特性进行更改,Liferay有许多内置的服务,比如用hook甚至可以覆盖Liferay服务. 可作为 ...

  2. Liferay7 BPM门户开发之17: Portlet 生命周期

    Portlet 生命周期 init() =〉 render() =〉 processAction() =〉 processEvent() =〉 serveResource() =〉destroy() ...

  3. Liferay7 BPM门户开发之10: 通用流程实现从Servlet到Portlet(Part1)

    开发目的: 实现通用流程自动化处理(即实现不需要hardcode代码的bpm统一处理后台,仅需要写少量前端html form代码和拖拽设计BPM定义) 既可独立运行或可依托于Liferay或依托其它门 ...

  4. Liferay7 BPM门户开发之26: 集成Activiti到Liferay7

    开发顺序: 实战任务1,开发BPM管理后台(用于在Liferay管理中心管理Activiti模型上传) 一个熟悉Portlet操作的项目,为开发打好基础. http://www.cnblogs.com ...

  5. Liferay7 BPM门户开发之12:acitiviti和liferay用户权限体系集成

    写到第12章才出现Liferay的内容,希望可以厚积薄发. 我们的目标是不使用不维护Activiti的用户组织架构,只维护Liferay的体系,这样的好处是非常明显的,即不用做组织架构的同步工作. 原 ...

  6. Liferay7 BPM门户开发之46: 集成Activiti用户、用户组、成员关系同步

    在实际的BPM集成开发过程中,Liferay和Activiti这两个异构的系统之间,用户.组的同步需求非常重要,用来实现签收组的概念,比如指定签收组.会签.抢签都需要用到. Activiti可以通过自 ...

  7. Liferay7 BPM门户开发之20: 理解Asset Framework

    Asset框架用于将您开发的门户内容添加Liferay的核心系统功能.打个比方,你开发了一个事件TodoList管理的插件,在列表显示的时候,你可以集成Asset框架,让你的自定义内容支持Tag标签. ...

  8. Liferay7 BPM门户开发之19: 理解Service Builder体系

    Service Builder是Liferay为业务开发而设计的模型驱动(model-driven)平台工具,提供一系列的实体类.数据持久化.服务相关的代码自动生成服务.支持Hibernate and ...

  9. Liferay7 BPM门户开发之15: Liferay开发体系简介

    Liferay SDK 开发体系 主要分6种: Portlet Hook Theme Layout Templates Web Modules Ext Portlet :类似于servlet的web组 ...

随机推荐

  1. 关于Datagridview控件用法的一些总结(设置列chicun)

    1. 关于Datagridview控件用法的一些总结:http://www.cnblogs.com/mingjiatang/p/4968049.html

  2. IIS mime类型 任意类型

    HTTP头   任意mime类型   .*    application/octet-stream

  3. servlet实现的三种方式对比(servlet 和GenericServlet和HttpServlet)

    第一种: 实现Servlet 接口 第二种: 继承GenericServlet 第三种 继承HttpServlet (开发中使用) 通过查看api文档发现他们三个(servlet 和GenericSe ...

  4. 韩服MU

    ※◆☆★☆◆※欢迎使用!!!如有问题或新功能需求请联系作者QQ:82850696*4*您使用的测试版已到期,如需继续使用,请联系作者 QQ : 82850696*0*2015-1-7 23:59:59 ...

  5. 20145225《Java程序设计》 实验三 "敏捷开发与XP实践"

    20145225<Java程序设计> 实验三 "敏捷开发与XP实践" 实验报告 实验内容 使用 git 上传代码 使用 git 相互更改代码 实现代码的重载 git 上 ...

  6. [[4], [5, 6, 7]](Python)list的方法

    现在我们要学习一些Python的数据结构了,本节将主要学习列表(list)的用法 1.list的方法 list.append(x) 在list的末尾添加一个元素 >>> a=[1,2 ...

  7. MapReduce: 一种简化的大规模集群数据处理法

    (只有文字没有图,图请参考http://research.google.com/archive/mapreduce.html) MapReduce: 一种简化的大规模集群数据处理法 翻译:风里来雨里去 ...

  8. Oracle创建表(包含、主键自增)

    注意:Oracle导出建表语句不会导出触发器及自增索引 第一步:创建一张表 create table member( memberId number primary key, --主键.自增长 mem ...

  9. Mac mysql修改密码

    在网上看了很多的办法,其实解决办法都对,只是没有说明白: 1,首先启动mysql服务 2,mysqladmin -uroot -p 'newpassword' 此时需要输入登陆密码(数据库的密码,刚安 ...

  10. Json&Razor&控制器

    JsonJson 属于JavaScript所以要书写在<script></script>中1.语法规则: 1.1:键值对 1.2:逗号分隔 1.3:花括号保存对象 1.4:方括 ...