第一步:修改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. libcore.io.GaiException: getaddrinfo failed: EAI_NODATA (No address associated with hostname)

    log报错如下: Caused by: libcore.io.GaiException: getaddrinfo failed: EAI_NODATA (No address associated w ...

  2. 单片机TM4C123学习(十):ADC采样模块

    1.头文件 #include "tiva_adc.h" // ADC 2.引脚 3.初始化 // ADC初始化 // 光敏电阻(PE0)为通道3,存在序列0中,硬件平均为8个点 a ...

  3. php数组函数

    1.键值函数 array_values()返回数组元素值,组成一个新的索引数组 2.array_keys()返回数组所有键名,组成一个索引数组 3.in_array()检查数组中是否存在某个值 4.a ...

  4. 尽量少用if else

    Michael Feathers是Object Mentor International公司的技术顾问.他的工作不仅是技术开发,他还参与对世界各地技术团队进行培训.指导等工作.他曾开发了将JUnit迁 ...

  5. java 开发常用的Linux命令

    1.查找文件 find / -name filename.txt 根据名称查找/目录下的filename.txt文件. find . -name "*.xml" 递归查找所有的xm ...

  6. java去处重复输出

    去除重复输出问题:   数组:大量相同数据类型的集合 数据类型[ ] 数组名=new 数据类型[长度] 数据类型[ ] 数组名=new 数据类型[ ]{值1,值 2,值3.....} 数据类型[ ] ...

  7. DBAPI部署

    1.添加源 sudo rpm -Uvh http://download.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm ...

  8. Subsets 子集系列问题 leetcode

    子集系列问题: Coding 问题中有时会出现这样的问题:给定一个集合,求出这个集合所有的子集(所谓子集,就是包含原集合中的一部分元素的集合). 或者求出满足一定要求的子集,比如子集中元素总和为定值, ...

  9. WPF RichTextBox,关键字搜索,样式改变,超链接替换,图文混排

    RichTextBox 只是一个控件,表示对 FlowDocument 对象执行操作的丰富编辑控件.它所承载的内容由其 Document 属性来呈现. Document 是一个 FlowDocumen ...

  10. 六款值得推荐的android(安卓)开源框架简介

    1.volley 项目地址 https://github.com/smanikandan14/Volley-demo (1)  JSON,图像等的异步下载: (2)  网络请求的排序(scheduli ...