Liferay7 BPM门户开发之32: 实现自定义认证登陆(定制Authentication Hook)
Posted 昕友软件开发
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Liferay7 BPM门户开发之32: 实现自定义认证登陆(定制Authentication Hook)相关的知识,希望对你有一定的参考价值。
第一步:修改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)的主要内容,如果未能解决你的问题,请参考以下文章
Liferay7 BPM门户开发之42: Liferay核心JSP定制扩展
Liferay7 BPM门户开发之41: Expando API入门
Liferay7 BPM门户开发之38: OSGi Bndtools开发入门
Liferay7 BPM门户开发之37: Liferay7下的OSGi Hook集成开发