在Spring MVC中创建双因素身份验证

时间:2015-08-24 05:57:27

标签: spring-mvc authentication spring-security

目前我们的webapps使用Spring Security(Acegi Security 1.0.3)进行用户名和密码验证。我最近被赋予了在框架中插入双因素身份验证(网格卡或令牌)的任务。

但是,Spring Security中有很多扩展点 - 所以如果你能给我一些关于我是否以正确的方式解决它的问题,我将不胜感激。

工作流程为:用户使用其用户名和密码登录>用户被发出网格卡挑战>验证成功后,他们将被授予访问权限。

这是我目前正在解决的解决方案。

  1. 首先,我创建了一个处理双因素身份验证的SimpleFormController(我称之为GridChallengeController)。如果成功,将为用户分配GridUser角色,并将重定向到他们最初请求的页面。
  2. 代码:

    Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    AcegiCrowdUserDetails details = (AcegiCrowdUserDetails) auth.getPrincipal();
    details.addAuthority("GridUser");
    UsernamePasswordAuthenticationToken existing = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
    SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(SecurityContextHolder.getContext().getAuthentication().getPrincipal(), 
    existing.getCredentials(), details.getAuthorities()));
    SavedRequest saved = (SavedRequest) request.getSession().getAttribute(AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY);
    return new ModelAndView(new RedirectView(saved.getFullRequestUrl()));
    
    1. 然后我扩展org.acegisecurity.ui.ExceptionTranslationFilter以允许另一个身份验证入口点。当accessDecisionManager抛出TwoFactorAccessDeniedException(扩展org.acegisecurity.AuthenticationException)时,它会被触发。以下是相关方法:
    2. 代码:

      @Override
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
          private AuthenticationEntryPoint twoFactorAuthenticationEntryPoint;
          ...
          try {
              chain.doFilter(request, response);
          } catch (TwoFactorAccessDeniedException ex) {
              handleException(request, response, chain, ex);
          } catch (AuthenticationException ex) {
              handleException(request, response, chain, ex);
          } catch (AccessDeniedException ex) {
              handleException(request, response, chain, ex);
          } catch (ServletException ex) {
          ...
          }
      }
      
      private void handleException(ServletRequest request, ServletResponse response, FilterChain chain, AcegiSecurityException exception) throws IOException, ServletException {
          if (exception instanceof TwoFactorAccessDeniedException) {
              sendStartTwoFactorAuthentication(request, response, chain, (AuthenticationException) exception);
          } else if (exception instanceof AuthenticationException) {
              sendStartAuthentication(request, response, chain, (AuthenticationException) exception);
          } else if (exception instanceof AccessDeniedException) {
          ...
          }
      }
      
      private void sendStartTwoFactorAuthentication(ServletRequest request, ServletResponse response, FilterChain chain, AuthenticationException reason) throws ServletException, IOException {
          twoFactorAuthenticationEntryPoint.commence((HttpServletRequest) request, (HttpServletResponse) response, reason);
      }
      
      1. 最后,当用户未通过双因素身份验证时,我会将org.acegisecurity.vote.AffirmativeBased扩展为TwoFactorAccessDeniedException(用户在使用其网卡进行身份验证时会被分配GridUser个角色)
      2. 代码:

        @Override
        public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config) throws AccessDeniedException {
            super.decide(authentication, object, config);
            FilterInvocation f = (FilterInvocation) object;
            if (!hasRole(authentication, "GridUser") && !hasRole(authentication, "ROLE_ANONYMOUS") && !f.getRequestUrl().equalsIgnoreCase("/gridchallenge.htm")) {
                throw new TwoFactorAccessDeniedException("Need a grid logon");
            }
        }
        

        以下是我的安全XML文件的相关摘要:

        代码:

        <bean id="exceptionTranslationFilter" class="authentication.TwoFactorExceptionTranslationFilter">
            <property name="authenticationEntryPoint">
                <bean id="basicProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
                    <property name="loginFormUrl" value="/acegilogin.jsp"/>
                    <property name="forceHttps" value="false"/>
                </bean>
            </property>
            <property name="twoFactorAuthenticationEntryPoint">
                <bean id="gridProcessingFilterEntryPoint" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint">
                    <property name="loginFormUrl" value="/gridchallenge.htm"/>
                    <property name="forceHttps" value="false"/>
                </bean>
            </property>
            <property name="accessDeniedHandler">
                <bean class="org.acegisecurity.ui.AccessDeniedHandlerImpl">
                    <property name="errorPage" value="/accessDenied.jsp"/>
                </bean>
            </property>
        </bean>
        
        <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
            <property name="authenticationManager" ref="authenticationManager"/>
            <property name="accessDecisionManager">
                <bean class="authentication.TwoFactorAffirmativeBased">
                    <property name="allowIfAllAbstainDecisions" value="false"/>
                    <property name="decisionVoters">
                        <list>
                            <ref bean="roleVoter"/>
                            <bean class="org.acegisecurity.vote.AuthenticatedVoter"/>
                        </list>
                    </property>
                </bean>
            </property>
            <property name="objectDefinitionSource">
                <value><![CDATA[
                    CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                    PATTERN_TYPE_APACHE_ANT
                    /admin/**=Vaas_Administrator
                    /guard/**=Guard
                    /acegilogin.jsp=IS_AUTHENTICATED_ANONYMOUSLY
                    /gridchallenge.htm=IS_AUTHENTICATED_FULLY
                    /**=VaasUser
                ]]></value>
            </property>
        </bean>
        

        我是否应该添加任何东西以使我的应用程序正常工作?

0 个答案:

没有答案