首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Spring安全中集成JCaptcha

如何在Spring安全中集成JCaptcha
EN

Stack Overflow用户
提问于 2012-10-12 03:05:18
回答 3查看 12K关注 0票数 4

在开始回答之前,我知道有一个更简单、更容易的ReCaptcha,但是我不能使用它。生产服务器没有联机。所以我们开始吧。

我使用Spring 3,在maven项目和weblogic上使用spring安全性作为web服务器(开发时丢弃)。我会在这件事上讲得很具体。

在查看我的配置和文件之前,我想向您展示我的问题列表:

  • 在ReCaptcha之前,我尝试过使用相同的编码结构的JCaptcha,它工作得很好。
  • logger.debug根本不出现在CaptchaCaptureFilter类和/或CaptchaVerifierFilter类中(而在ArtajasaAuthenticationProvider类中)。
  • 我可以看到captcha图像,但是不管答案是什么,它总是无效的。
  • 在当前状态下,它不能在jetty或weblogic中工作,但是如果我将自定义筛选器位置更改为下面的位置,它只在jetty中工作。

谢谢收看,非常感谢你回答我的问题。下面是详细情况。

JCaptcha的存储库是这样的:

代码语言:javascript
复制
<repository>
        <id>sourceforge-releases</id>
        <name>Sourceforge Releases</name>
        <url>https://oss.sonatype.org/content/repositories/sourceforge-releases</url>
    </repository>

<dependency>
        <groupId>com.octo.captcha</groupId>
        <artifactId>jcaptcha-integration-simple-servlet</artifactId>
        <version>2.0-alpha-1</version>
    </dependency>

下面是我在.xml文件中所做的一些配置:

web.xml

代码语言:javascript
复制
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
        /WEB-INF/applicationContext.xml
        /WEB-INF/spring/spring-security.xml
    </param-value>
</context-param>
 <listener>
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>

<servlet>
    <servlet-name>jcaptcha</servlet-name>
    <servlet-class>com.octo.captcha.module.servlet.image.SimpleImageCaptchaServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>jcaptcha</servlet-name>
    <url-pattern>/jcaptcha.jpg</url-pattern>
</servlet-mapping>

spring-security.xml

代码语言:javascript
复制
<http auto-config="true" use-expressions="true">
    <intercept-url pattern="/resources/**" access="permitAll()" />
    <intercept-url pattern="/jcaptcha.jpg" access="permitAll()" />
    <intercept-url pattern="/**" access="isAuthenticated()" />

    <form-login login-page="/session/login/" default-target-url="/"
                authentication-failure-url="/session/loginfailed/" />
    <logout logout-success-url="/session/logout/" />
    <access-denied-handler error-page="/session/403/" />

    <!--JCaptcha Filtering-->
    <custom-filter ref="captchaCaptureFilter" before="FORM_LOGIN_FILTER"/>
    <custom-filter ref="captchaVerifierFilter" after="FORM_LOGIN_FILTER"/>
    <anonymous />
</http>

<!-- For capturing CAPTCHA fields -->
<beans:bean id="captchaCaptureFilter" class="com.util.CaptchaCaptureFilter" />

<!-- For verifying CAPTCHA fields -->
<!-- Private key is assigned by the JCaptcha service -->
<beans:bean id="captchaVerifierFilter" class="com.util.CaptchaVerifierFilter"
      p:failureUrl="/session/loginfailed/"
      p:captchaCaptureFilter-ref="captchaCaptureFilter"/>

<beans:bean id="customAuthFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <beans:property name="sessionAuthenticationStrategy" ref="sas"/>
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="allowSessionCreation" value="true" />
</beans:bean>

<beans:bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
    <beans:constructor-arg name="sessionRegistry" ref="sessionRegistry"/>
    <beans:property name="maximumSessions" value="1" />
</beans:bean>

<beans:bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />

<beans:bean id="userService" class="com.service.mybatis.UserManager" />

<beans:bean id="customAuthenticationProvider" class="com.util.ArtajasaAuthenticationProvider" />
<authentication-manager alias="authenticationManager">
    <authentication-provider ref="customAuthenticationProvider" />
</authentication-manager>

<beans:bean id="accessDeniedHandler" class="com.util.ThouShaltNoPass">
    <beans:property name="accessDeniedURL" value="/session/403/" />
</beans:bean>

这些是java类:

ArtajasaAuthenticationProvider.java

代码语言:javascript
复制
public class ArtajasaAuthenticationProvider implements AuthenticationProvider {

@Autowired
private UserService userService;
private Logger logger = LoggerFactory.getLogger(ArtajasaAuthenticationProvider.class);

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String username = String.valueOf(authentication.getPrincipal());
    String password = String.valueOf(authentication.getCredentials());
    logger.debug("Checking authentication for user {}", username);
    if (StringUtils.isBlank(username)
            || StringUtils.isBlank(password)) {
        throw new BadCredentialsException("No Username and/or Password Provided.");
    } else {
        Pengguna user = userService.select(username);
        if (user == null) {
            throw new BadCredentialsException("Invalid Username and/or Password.");
        }
        if (user.getPassword().equals(new PasswordUtil().generateHash(password, user.getSalt()))) {
            List<GrantedAuthority> authorityList = (List<GrantedAuthority>) userService.getAuthorities(user);
            return new UsernamePasswordAuthenticationToken(username, password, authorityList);
        } else {
            throw new BadCredentialsException("Invalid Username and/or Password.");
        }
    }
}

@Override
public boolean supports(Class<?> authentication) {
    return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}

CaptchaCaptureFilter.java

代码语言:javascript
复制
public class CaptchaCaptureFilter extends OncePerRequestFilter {

protected Logger logger = Logger.getLogger(CaptchaCaptureFilter.class);
private String userCaptchaResponse;
private HttpServletRequest request;

@Override
public void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
                             FilterChain chain) throws IOException, ServletException {

    logger.debug("Captcha capture filter");

    // Assign values only when user has submitted a Captcha value.
    // Without this condition the values will be reset due to redirection
    // and CaptchaVerifierFilter will enter an infinite loop
    if (req.getParameter("jcaptcha") != null) {
        request = req;
        userCaptchaResponse = req.getParameter("jcaptcha");
    }

    logger.debug("userResponse: " + userCaptchaResponse);

    // Proceed with the remaining filters
    chain.doFilter(req, res);
}

public String getUserCaptchaResponse() {
    return userCaptchaResponse;
}

public void setUserCaptchaResponse(String userCaptchaResponse) {
    this.userCaptchaResponse = userCaptchaResponse;
}

public HttpServletRequest getRequest() {
    return request;
}

public void setRequest(HttpServletRequest request) {
    this.request = request;
}
}

CaptchaVerifierFilter.java

代码语言:javascript
复制
public class CaptchaVerifierFilter extends OncePerRequestFilter {

protected Logger logger = Logger.getLogger(CaptchaVerifierFilter.class);
private String failureUrl;
private CaptchaCaptureFilter captchaCaptureFilter;

// Inspired by log output: AbstractAuthenticationProcessingFilter.java:unsuccessfulAuthentication:320)
// Delegating to authentication failure handlerorg.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@15d4273
private SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();

@Override
public void doFilterInternal(HttpServletRequest req, HttpServletResponse res,
                             FilterChain chain) throws IOException, ServletException {

    logger.debug("Captcha verifier filter");
    logger.debug("userResponse: " + captchaCaptureFilter.getUserCaptchaResponse());

    // Assign values only when user has submitted a Captcha value
    if (captchaCaptureFilter.getUserCaptchaResponse() != null) {

        // Send HTTP request to validate user's Captcha
        boolean captchaPassed = SimpleImageCaptchaServlet.validateResponse(captchaCaptureFilter.getRequest(), captchaCaptureFilter.getUserCaptchaResponse());

        // Check if valid
        if (!captchaPassed) {
            logger.debug("Captcha is invalid!");

            // Redirect user to login page
            failureHandler.setDefaultFailureUrl(failureUrl);
            failureHandler.onAuthenticationFailure(req, res, new BadCredentialsException("Captcha invalid! " + captchaCaptureFilter.getRequest() + " " + captchaCaptureFilter.getUserCaptchaResponse()));

        } else {
            logger.debug("Captcha is valid!");
        }

        // Reset Captcha fields after processing
        // If this method is skipped, everytime we access a page
        // CaptchaVerifierFilter will infinitely send a request to the Google Captcha service!
        resetCaptchaFields();
    }

    // Proceed with the remaining filters
    chain.doFilter(req, res);
}

/**
 * Reset Captcha fields
 */
public void resetCaptchaFields() {
    captchaCaptureFilter.setUserCaptchaResponse(null);
}

public String getFailureUrl() {
    return failureUrl;
}

public void setFailureUrl(String failureUrl) {
    this.failureUrl = failureUrl;
}

public CaptchaCaptureFilter getCaptchaCaptureFilter() {
    return captchaCaptureFilter;
}

public void setCaptchaCaptureFilter(CaptchaCaptureFilter captchaCaptureFilter) {
    this.captchaCaptureFilter = captchaCaptureFilter;
}
}

最后但并非最不重要的,login.jsp

代码语言:javascript
复制
<%@ taglib prefix='c' uri='http://java.sun.com/jstl/core_rt' %>

<form id="login" name="f" action="<c:url value='/j_spring_security_check'/>" method="POST">
  <div class="container">

    <div class="content">
        <div class="row">
            <div class="login-form">
                <h3>Login</h3>
                <br />
                  <fieldset>
                       <div class="clearfix">
                            username: ecr
                            <input type="text" name='j_username' value='<c:if test="${not empty param.login_error}"><c:out value="${SPRING_SECURITY_LAST_USERNAME}"/></c:if>' placeholder="username@artajasa.co.id">
                       </div>
                       <div class="clearfix">
                           password: ecr123
                           <input type="password" name='j_password' placeholder="password">
                       </div>
                       <div class="clearfix">
                           <img src="../../jcaptcha.jpg" />
                           <br />
                           <input type="text" name="jcaptcha" placeholder="masukkan captcha" />
                       </div>
                       <br />
                       <button class="btn btn-primary" type="submit"><i class="icon-lock"></i> Sign in</button>
                   </fieldset>
            </div>
        </div>
    </div>
     <br />
     <c:if test="${not empty error}">
            <div class="alert alert-error">
            <button type="button" class="close" data-dismiss="alert"><i class="icon-remove"></i></button>
            Login Failed, try again.<br />
            <c:out value="${sessionScope['SPRING_SECURITY_LAST_EXCEPTION'].message}"/>
            </div>
          </c:if>
  </div>

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-10-15 08:22:15

问题解决了!我找到了答案。所以我们毕竟不需要CaptchaVerifierFilter。我在AuthenticationProvider中验证了captcha。

以下是更改的清单:

在Springsecurity.xml中,这个

代码语言:javascript
复制
<!--JCaptcha Filtering-->
<custom-filter ref="captchaCaptureFilter" before="FORM_LOGIN_FILTER"/>
<custom-filter ref="captchaVerifierFilter" after="FORM_LOGIN_FILTER"/>

变成这个

代码语言:javascript
复制
<!--JCaptcha Filtering-->
<custom-filter ref="captchaCaptureFilter" before="FORM_LOGIN_FILTER"/>

删除

代码语言:javascript
复制
<!-- For verifying CAPTCHA fields -->
<!-- Private key is assigned by the JCaptcha service -->
<beans:bean id="captchaVerifierFilter" class="com.util.CaptchaVerifierFilter"
  p:failureUrl="/session/loginfailed/"
  p:captchaCaptureFilter-ref="captchaCaptureFilter"/>

并在这里验证captcha

代码语言:javascript
复制
<beans:bean id="customAuthenticationProvider" class="com.pusilkom.artajasa.ecr.backend.util.MyAuthenticationProvider"
            p:captchaCaptureFilter-ref="captchaCaptureFilter"/>
票数 3
EN

Stack Overflow用户

发布于 2016-05-19 05:37:13

我不知道这是不是正确的做法,但它对我来说是完美的。我创建了与您的类相同的类,过滤器类代码中的更改很少,security-context.xml.中的更改也很小。

  1. 我在CaptchaCaptureFilter中验证了captcha,并将验证结果存储在变量iscaptchaPassed中,这是CaptchaCaptureFilter中的一个属性。除了iscaptchaPassed之外,我还将响应存储为captchaResponse,以检查以后在CaptchaVerifierFilter中是否为null。

公共类CaptchaCaptureFilter扩展OncePerRequestFilter {

代码语言:javascript
复制
private String captchaResponse;

private boolean iscaptchaPassed;

//setters和getter

代码语言:javascript
复制
@Override
public void doFilterInternal(HttpServletRequest req, HttpServletResponse res,FilterChain chain) throws IOException, ServletException {

    logger.info("Captcha capture filter");

    String captcha_Response=req.getParameter("jcaptcha");
    logger.info("response captcha captured : " +captcha_Response);

    if(captcha_Response!=null)
    {
        iscaptchaPassed = SimpleImageCaptchaServlet.validateResponse(req, req.getParameter("jcaptcha"));
        captchaResponse=captcha_Response;
        logger.info("isCaptchaPassed value is "+iscaptchaPassed);
    }

    // Proceed with the remaining filters
    chain.doFilter(req, res);
}
  1. 按以下方式读取注入到captchaCaptureFilter中的CaptchaVerifyFilter的值和句柄。

公共类CaptchaVerifierFilter扩展OncePerRequestFilter {

代码语言:javascript
复制
protected Logger logger = LoggerFactory.getLogger(Filter.class);

private CaptchaCaptureFilter captchaCaptureFilter;

private String failureUrl;

//getters and setters**strong text**

private SimpleUrlAuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();

@Override
public void doFilterInternal(HttpServletRequest req, HttpServletResponse res,FilterChain chain) throws IOException, ServletException {

    //logger.info("Captcha verifier filter");
    boolean captchaPassed=captchaCaptureFilter.getIscaptchaPassed();
    String captchaResponse=captchaCaptureFilter.getCaptchaResponse();
    //logger.info("captcha captured :"+captchaResponse+" validation result of captcha : " +captchaPassed);

    if(captchaResponse!=null)
    {
        if(captchaPassed)
        {
            logger.info("Captcha is valid!");
        }
        else
        {
            logger.info("Captcha is invalid!");
            failureHandler.setDefaultFailureUrl(failureUrl);
            failureHandler.onAuthenticationFailure(req, res, new BadCredentialsException("Captcha invalid!"));
        }
        resetCaptchaFields();   
    }

    chain.doFilter(req, res);           
}

/** 
 * Reset Captcha fields
 */
public void resetCaptchaFields() {
    captchaCaptureFilter.setCaptchaResponse(null);
    captchaCaptureFilter.setIscaptchaPassed(false);;
}
  1. 在Securitycontext.xml中更改以下内容。

安全性:自定义筛选器ref="captchaCaptureFilter“before=优先

安全性:自定义过滤器ref="captchaVerifierFilter“after="FORM_LOGIN_FILTER”

我在验证captcha的所有过滤器之前添加captchaCaptureFilter。验证结果在FORM_LOGIN_FILTER. UserNameAndPasswordAuthFilter之后使用。

票数 1
EN

Stack Overflow用户

发布于 2015-12-25 12:30:05

我又尝试了一种验证JCaptcha的方法,即在Security身份验证之前。首先,JCaptcha将得到验证,如果正确,控制将转到。我也没有在Securitycontext.xml中添加CaptchaCaptureFilter。

下面是我试过的。它正常工作了。

代码语言:javascript
复制
public String login() {
    this.captchaPassed = false;

    // Check if captcha entered is correct. If yes then only proceed with
    // Spring Security Authentication
    this.captchaPassed = checkLoginCaptcha();

    if (captchaPassed) {
        boolean success = authenticationService.login(userName, password);

        if (success) {
            StringBuilder userNameBuilder = new StringBuilder();
            userNameBuilder.append(userName);
            FacesContext.getCurrentInstance().getExternalContext()
                    .getSessionMap()
                    .put("USER_NAME_PARAM", userNameBuilder.toString());
            return ApplicationConstants.HOME_PAGE;
        } else {
            this.message = "Wrong Username or Password Entered. Please LOGIN again.";
            this.userName = null;
            this.password = null;
            this.captchaString = null;

            return ApplicationConstants.LOGIN_PAGE;
        }
    } else {
        this.message = "Invalid captcha entered. Please LOGIN again.";
        return ApplicationConstants.LOGIN_PAGE;
    }
}

public boolean checkLoginCaptcha() {

    HttpServletRequest req = (HttpServletRequest) FacesContext
            .getCurrentInstance().getExternalContext().getRequest();
    String str = null;
    boolean flag = false;

    try {
        str = req.getParameter("loginForm:jcaptchaString");

        if (str != null) {
            flag = SimpleImageCaptchaServlet.validateResponse(req, str);
        }
    } catch (Exception e) {
        e.printStackTrace();
        flag = false;
    }

    return flag;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/12851637

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档