zoukankan      html  css  js  c++  java
  • How to Integrate JCaptcha in Spring Security

    The repository for JCaptcha is this one:

    <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>

    Here are some configuration I made in .xml files:

    web.xml

    <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

    <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"/>
        <!-- REMOVED 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 -->
    <!-- REMOVED beans:bean id="captchaVerifierFilter" class="com.util.CaptchaVerifierFilter"
          p:failureUrl="/session/loginfailed/"
          p:captchaCaptureFilter-ref="captchaCaptureFilter"/-->
    
        <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.MyAuthenticationProvider" p:captchaCaptureFilter-ref="captchaCaptureFilter" />
    <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>

    And these are the java classes:

    MyAuthenticationProvider.java

    public class MyAuthenticationProvider implements AuthenticationProvider {
    
    @Autowired
    private UserService userService;
    private Logger logger = LoggerFactory.getLogger(ArtajasaAuthenticationProvider.class);
    private CaptchaCaptureFilter captchaCaptureFilter;
    
    @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);
        logger.debug("userResponse: {}", captchaCaptureFilter.getUserCaptchaResponse());
        if (StringUtils.isBlank(username)
                || StringUtils.isBlank(password)) {
            throw new BadCredentialsException("No Username and/or Password Provided.");
        }
        else if(StringUtils.isBlank(captchaCaptureFilter.getUserCaptchaResponse())) {
            throw new BadCredentialsException("Captcha Response is Empty");
        }
        else {
            // 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 valid!");
                resetCaptchaFields();
    
                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.");
                }
            }
            else {
                logger.debug("Captcha is invalid!");
                resetCaptchaFields();
    
                throw new BadCredentialsException("Invalid Captcha.");
            }
        }
    }
    
    @Override
    public boolean supports(Class<?> authentication) {
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }
    
    /**
     * Reset Captcha fields
     */
    public void resetCaptchaFields() {
        captchaCaptureFilter.setUserCaptchaResponse(null);
    }
    
    public CaptchaCaptureFilter getCaptchaCaptureFilter() {
        return captchaCaptureFilter;
    }
    
    public void setCaptchaCaptureFilter(CaptchaCaptureFilter captchaCaptureFilter) {
        this.captchaCaptureFilter = captchaCaptureFilter;
    }
    }

    CaptchaCaptureFilter.java

    public class CaptchaCaptureFilter extends OncePerRequestFilter {
    
    private 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;
    }
    }

    Last but not least, login.jsp

    <%@ 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>

    done!

  • 相关阅读:
    日记 2018/1/12
    【程序员笔试面试必会——排序①】Python实现 冒泡排序、选择排序、插入排序、归并排序、快速排序、堆排序、希尔排序
    Python笔试、面试 【必看】
    高性能Go并发
    Go连接MySql数据库Error 1040: Too many connections错误解决
    MAC 配置文件 ~/.zshrc
    go-statsd项目
    日记 2017.11.20
    sed 命令详解
    Opentsdb简介(一)
  • 原文地址:https://www.cnblogs.com/sos-blue/p/3362590.html
Copyright © 2011-2022 走看看