Jira每次登录显示验证码

背景:在Jira的生产环境中,由于各种原因,不能直接设置密码策略(比如,用户来自LDAP),存在不少用户的密码设置过于简单,容易导致机器人的攻击,带来安全隐患。另外,由于历史诸如Remote API调用的原因,Jira也不提供登录页面直接提供验证码的功能,这使得安全隐患进一步暴露。


本文尝试对Jira源码进行一定的修改,使得用户首次登录时强行出现验证码。当然也可以通过扩展Jira的安全验证接口,通过插件的形式发布Plugin。


在购买Jira产品后,厂家会有一份对应的Jira源码,有关安全登录的有两个类,分别是LoginInfoImpl.java,LoginServiceImpl.java


1,修改LoginInfoImpl.java的构造函数


public LoginInfoImpl(final Long lastLoginTime, final Long previousLoginTime, final Long lastFailedLoginTime, final Long loginCount,
            final Long currentFailedLoginCount, final Long totalFailedLoginCount, final Long maxAuthenticationAttemptsAllowed, final boolean elevatedSecurityCheckRequired)
    {
        this.lastLoginTime = lastLoginTime;
        this.previousLoginTime = previousLoginTime;
        this.lastFailedLoginTime = lastFailedLoginTime;
        this.loginCount = loginCount;
        this.currentFailedLoginCount = currentFailedLoginCount;
        this.totalFailedLoginCount = totalFailedLoginCount;
        this.maxAuthenticationAttemptsAllowed = maxAuthenticationAttemptsAllowed;
        this.elevatedSecurityCheckRequired = true;//elevatedSecurityCheckRequired;
    }


完整代码:


package com.atlassian.jira.bc.security.login;

import org.apache.commons.lang.builder.ToStringBuilder;


/**
 * @since v4.0.1
 */
public class LoginInfoImpl implements LoginInfo
{
    private final Long lastLoginTime;
    private final Long previousLoginTime;
    private final Long loginCount;
    private final Long currentFailedLoginCount;
    private final Long totalFailedLoginCount;
    private final Long lastFailedLoginTime;
    private final boolean elevatedSecurityCheckRequired;
    private final Long maxAuthenticationAttemptsAllowed;

    public LoginInfoImpl(final Long lastLoginTime, final Long previousLoginTime, final Long lastFailedLoginTime, final Long loginCount,
            final Long currentFailedLoginCount, final Long totalFailedLoginCount, final Long maxAuthenticationAttemptsAllowed, final boolean elevatedSecurityCheckRequired)
    {
        this.lastLoginTime = lastLoginTime;
        this.previousLoginTime = previousLoginTime;
        this.lastFailedLoginTime = lastFailedLoginTime;
        this.loginCount = loginCount;
        this.currentFailedLoginCount = currentFailedLoginCount;
        this.totalFailedLoginCount = totalFailedLoginCount;
        this.maxAuthenticationAttemptsAllowed = maxAuthenticationAttemptsAllowed;
        this.elevatedSecurityCheckRequired = true;//elevatedSecurityCheckRequired;
    }

    public LoginInfoImpl(final LoginInfo loginInfo, final boolean elevatedSecurityCheckRequired)
    {
        this(loginInfo.getLastLoginTime(), loginInfo.getPreviousLoginTime(), loginInfo.getLastFailedLoginTime(), loginInfo.getLoginCount(),
                loginInfo.getCurrentFailedLoginCount(), loginInfo.getTotalFailedLoginCount(), loginInfo.getMaxAuthenticationAttemptsAllowed(), elevatedSecurityCheckRequired);
    }

    public Long getMaxAuthenticationAttemptsAllowed()
    {
        return maxAuthenticationAttemptsAllowed;
    }

    public Long getLastLoginTime()
    {
        return lastLoginTime;
    }

    public Long getPreviousLoginTime()
    {
        return previousLoginTime;
    }

    public Long getLoginCount()
    {
        return loginCount;
    }

    public Long getCurrentFailedLoginCount()
    {
        return currentFailedLoginCount;
    }

    public Long getTotalFailedLoginCount()
    {
        return totalFailedLoginCount;
    }

    public Long getLastFailedLoginTime()
    {
        return lastFailedLoginTime;
    }

    public boolean isElevatedSecurityCheckRequired()
    {
        System.out.println("===replace isElevatedSecurityCheckRequired");
    	return elevatedSecurityCheckRequired;
    }

    @Override
    public String toString()
    {
        return ToStringBuilder.reflectionToString(this);
    }
}




2、修改LoginServiceImpl.java的isElevatedSecurityCheckAlwaysShown()方法


public boolean isElevatedSecurityCheckAlwaysShown()
    {
        //return loginManager.isElevatedSecurityCheckAlwaysShown();
    	return true;
    }


package com.atlassian.jira.bc.security.login;

import com.atlassian.crowd.embedded.api.User;
import com.atlassian.jira.config.properties.APKeys;
import com.atlassian.jira.config.properties.ApplicationProperties;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.security.login.LoginManager;
import com.atlassian.jira.user.util.UserManager;
import com.atlassian.jira.util.JiraContactHelper;
import com.atlassian.jira.util.JiraUtils;
import com.atlassian.jira.util.http.JiraUrl;
import com.atlassian.seraph.auth.AuthenticationErrorType;
import com.atlassian.seraph.filter.BaseLoginFilter;
import com.atlassian.seraph.filter.LoginFilterRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import static com.atlassian.jira.util.dbc.Assertions.notNull;

/**
 * Implementation of LoginManager
 *
 * @since v4.0.1
 */
public class LoginServiceImpl implements LoginService
{
    private final LoginManager loginManager;
    private final ApplicationProperties applicationProperties;
    private final UserManager userManager;
    private final JiraContactHelper contactHelper;
    private final JiraAuthenticationContext authenticationContext;

    public LoginServiceImpl(LoginManager loginManager, ApplicationProperties applicationProperties,
            UserManager userManager, JiraContactHelper contactHelper, JiraAuthenticationContext authenticationContext)
    {
        this.contactHelper = contactHelper;
        this.authenticationContext = authenticationContext;
        this.applicationProperties = notNull("applicationProperties", applicationProperties);
        this.loginManager = notNull("loginManager", loginManager);
        this.userManager = notNull("userManager", userManager);
    }

    public LoginInfo getLoginInfo(final String userName)
    {
        return loginManager.getLoginInfo(userName);
    }

    public boolean isElevatedSecurityCheckAlwaysShown()
    {
        //return loginManager.isElevatedSecurityCheckAlwaysShown();
    	return true;
    }

    public void resetFailedLoginCount(final User user)
    {
        loginManager.resetFailedLoginCount(user);
    }

    public LoginResult authenticate(final User user, final String password)
    {
        return loginManager.authenticate(user, password);
    }

    public void logout(final HttpServletRequest request, final HttpServletResponse response)
    {
        loginManager.logout(request, response);
    }

    public LoginProperties getLoginProperties(final User remoteUser, final HttpServletRequest request)
    {
        notNull("request", request);

        // see loginform.jsp for example of how this needs to work
        final LoginResult lastLoginResult = (LoginResult) request.getAttribute(LoginService.LOGIN_RESULT);

        final LoginInfo loginInfo = lastLoginResult == null ? null : lastLoginResult.getLoginInfo();

        final boolean loginSucceeded = remoteUser != null;
        final boolean loginError = BaseLoginFilter.LOGIN_ERROR.equals(LoginFilterRequest.getAuthenticationStatus(request));
        final boolean communicationError = AuthenticationErrorType.CommunicationError.equals(LoginFilterRequest.getAuthenticationErrorType(request));
        final boolean captchaFailure = (lastLoginResult != null && lastLoginResult.getReason() == LoginReason.AUTHENTICATION_DENIED);
        final boolean loginFailedCausedByPermissions = (lastLoginResult != null && lastLoginResult.getReason() == LoginReason.AUTHORISATION_FAILED);
        final boolean isElevatedSecurityCheckShown = isElevatedSecurityCheckShown(loginInfo);

        if (LoginLoggers.LOGIN_GADGET_LOG.isDebugEnabled())
        {
            LoginLoggers.LOGIN_GADGET_LOG.debug("Gadget login called with lastLoginResult : " + String.valueOf(lastLoginResult));
        }

        return LoginProperties.builder().
                loginSucceeded(loginSucceeded).
                loginError(loginError).
                communicationError(communicationError).
                allowCookies(applicationProperties.getOption(APKeys.JIRA_OPTION_ALLOW_COOKIES)).
                externalPasswordManagement(!userManager.hasPasswordWritableDirectory()).
                externalUserManagement(applicationProperties.getOption(APKeys.JIRA_OPTION_USER_EXTERNALMGT)).
                isPublicMode(isPublicMode()).
                isElevatedSecurityCheckShown(isElevatedSecurityCheckShown).
                captchaFailure(captchaFailure).
                loginFailedByPermissions(loginFailedCausedByPermissions).
                contactAdminLink(getContactLink(request)).
                build();
    }

    private String getContactLink(HttpServletRequest request)
    {
        return contactHelper.getAdministratorContactLinkHtml(JiraUrl.constructBaseUrl(request), authenticationContext.getI18nHelper());
    }

    boolean isPublicMode()
    {
        return JiraUtils.isPublicMode();
    }

    private boolean isElevatedSecurityCheckShown(final LoginInfo loginInfo)
    {
        return isElevatedSecurityCheckAlwaysShown() || (loginInfo != null && loginInfo.isElevatedSecurityCheckRequired());
    }
}



3、在Maven的pom.xml中引入Atlassian Jira 依赖,并进行编译


<dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-api</artifactId>
            <version>${jira.version}</version>
            <scope>provided</scope>
        </dependency>

<dependency>
            <groupId>com.atlassian.jira</groupId>
            <artifactId>jira-core</artifactId>
            <version>${jira.version}</version>
            <scope>provided</scope>
        </dependency>


<properties>
        <jira.version>6.3.14</jira.version>
        <spring.version>2.5.6</spring.version>
        <amps.version>5.0.13</amps.version>
        <spring.osgi.version>1.2.1</spring.osgi.version>
        <plugin.testrunner.version>1.2.3</plugin.testrunner.version>
        <!-- TestKit version 5.x for JIRA 5.x, 6.x for JIRA 6.x -->
        <testkit.version>5.2.26</testkit.version>
    </properties>


4、把编译的class文件拷贝atlassian-jira-6.3.15-standalone/atlassian-jira/WEB-INF/classes/com/atlassian/jira/bc/security/login,并重启Jira







阅读更多
个人分类: Atlassian Jira
想对作者说点什么? 我来说一句

Jira6.3.6任意插件破解

2016年11月16日 5.16MB 下载

没有更多推荐了,返回首页

不良信息举报

Jira每次登录显示验证码

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭