第一步:修改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. iOS button 里边的 字体的 摆放

    button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; button.titleEdgeInsets ...

  2. Selenium2+python自动化8-SeleniumBuilder辅助定位元素

    前言 福利来了,对于用火狐浏览器的小伙伴们,你还在为定位元素而烦恼嘛? 上古神器Selenium Builder来啦,哪里不会点哪里,妈妈再也不用担心我的定位元素问题啦!(但是也不是万能,基本上都能覆 ...

  3. error C2065: 'INVALID_SET_FILE_POINTER' : undeclared identifier

    Searching MSDN for that constant brings up one result: it's a failure code for SetFilePointer() and ...

  4. Odoo下拉动作列表

  5. 学习python之练习(二)

    #2.已知a1=1,a2=2,an=a(n-1)+a(n-2)(n>=3),求数列{a1,a2,a3....an}的总和 import math arr = [0]*100 num = 0 fo ...

  6. Oracle中的带参数的视图--我们致力于打造人力资源软件

    创建包和包体 create or replace package pkg_pv is procedure set_pv(pv varchar2); function get_pv return var ...

  7. Mysql --分区(3)range分区

    3.分区类型 RANGE分区 按照range分区的表是利用取值范围将数据分成分区,区间要连续并且不能互相重叠,使用values less than操作符进行分区定义 CREATE TABLE tnp ...

  8. Xml序列化UTF-8格式错误

    我需要得到一个类的Xml序列化后的字符串 using (System.IO.MemoryStream mem = new System.IO.MemoryStream()) { XmlTextWrit ...

  9. ubuntu系统 刷bios

    bios放电后,出现不能识别硬盘.进入bios将Boot设为从UEFI: ubuntu启动.

  10. Duang的成长——使用造字程序输入生僻字

    使用造字程序输入生僻字 最近,一个字突然间火了起来,那就是——duang! (图片来自网络) 那么,问题来了!造字程序哪家强?(此处有掌声) 其实,微软早就考虑到各国文字的博大精深,在系统中集成了一个 ...