2014-03-27 37 views
4

我一直在试图用jsf中的primefaces实现一些基本的推送功能。我在那里使用了反例http://www.primefaces.org/showcase-labs/push/counter.jsf。本质上它是一个增加共享计数器的按钮。运行这个例子的时候,我总是得到这个错误:JSF“错误Mac没有验证!”

ERROR: MAC did not verify! 

我的理解是,一个MAC生成每一个会话,然后根据检查每个传入消息来验证源没有改变(我认为)。我一直没能找到这个原因,并已经看过其他线程如:

ERROR: MAC did not verify! PrimeFaces

JSF: Mojarra 2.1 to 2.2 migration causing ViewExpiredException

可惜这些都没有解决我的问题。这两个似乎是由我没有得到的ViewExpiredException引起的。我发现来阻止它的唯一的事情就是从客户端更改状态保存方法服务器在web.xml:

<param-name>javax.faces.STATE_SAVING_METHOD</param-name> 
<param-value>client</param-value> 

但是这样做柜台不再共享时,但似乎每个用户,这不是我想要的。我的最终目标是实现一个聊天室,这个聊天室大部分都在那里,但是现在它使用的短时间轮询不是很容易扩展。看过primefaces后,我认为这会是理想的,但一直在努力使用它。

我试过在多个Web服务器(Tomcat,Jetty和Glassfish)上尝试过使用不同版本的JSF(Mojarra)和primefaces(3.4和4.0)版本。我已经在多个浏览器和多台计算机上测试过它。有时我可以在出现错误之前递增柜台几次,有时会立即发生。我没有得到任何例外或服务错误,一切都编译完成。我还想提一下,我在其他项目中遇到过此错误,但在重新启动服务器后它已消失。当使用primefaces时,它总是会发生。任何帮助将不胜感激。

编辑

当离开状态保存到服务器在web.xml中避免MAC错误,我已经注意到,共用的对从同一台机器上每个浏览器的基础工作。这意味着如果我有多个选项卡或窗口,则更新计数器的一次更新。但它并不适用于所有浏览器,Firefox中的计数器更改并未反映在Chrome或IE中,或其他方式。如果在两台独立的计算机上,它也不会反映出来。我不知道这是否有帮助,但我想我会提到它。

编辑

注意到,本例中的bean是会话范围我改成了应用程序作用域后。当然,会话范围意味着每个浏览器都有自己的副本。现在,这些更改反映在浏览器和机器上。回到我原来的问题,我仍然想知道为什么将保存状态更改为服务器可以修复MAC错误,以及这会产生什么影响?我假设服务器现在必须维护每个会话的视图状态,而不是客户端,可扩展性更低/客户端服务器流量更少?从我读过的内容可以看出,如果将保存状态设置为服务器,则无法检查视图过期异常或阻止用户创建视图(如果它们已经有太多),这是正确的吗?

+0

根据重复,您需要配置客户端状态保存密钥。或者,如果您在服务器群集上运行,则需要确保使用粘性节点。 – BalusC

回答

0

看来你的应用程序中有依赖性问题 ViewExpiredException可以很容易地处理,这是没有问题的 handle ViewExpiredException

全配置JSF项目例如JSF2.2 frontend

+0

第一个链接已损坏,请添加工作链接 – aName

-4

ByteArrayGuard类随机输出“ERROR:MAC没有验证! “消息到错误控制台。只有在启用客户端视图状态加密并且针对服务器运行多个客户端时才会发生这种情况。

所以,你必须改变类在Java服务器面临着引用...

/* 
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 
* 
* Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved. 
* 
* The contents of this file are subject to the terms of either the GNU 
* General Public License Version 2 only ("GPL") or the Common Development 
* and Distribution License("CDDL") (collectively, the "License"). You 
* may not use this file except in compliance with the License. You can 
* obtain a copy of the License at 
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html 
* or packager/legal/LICENSE.txt. See the License for the specific 
* language governing permissions and limitations under the License. 
* 
* When distributing the software, include this License Header Notice in each 
* file and include the License file at packager/legal/LICENSE.txt. 
* 
* GPL Classpath Exception: 
* Oracle designates this particular file as subject to the "Classpath" 
* exception as provided by Oracle in the GPL Version 2 section of the License 
* file that accompanied this code. 
* 
* Modifications: 
* If applicable, add the following below the License Header, with the fields 
* enclosed by brackets [] replaced by your own identifying information: 
* "Portions Copyright [year] [name of copyright owner]" 
* 
* Contributor(s): 
* If you wish your version of this file to be governed by only the CDDL or 
* only the GPL Version 2, indicate your decision by adding "[Contributor] 
* elects to include this software in this distribution under the [CDDL or GPL 
* Version 2] license." If you don't indicate a single choice of license, a 
* recipient has the option to distribute your version of this file under 
* either the CDDL, the GPL Version 2 or to extend the choice of license to 
* its licensees as provided above. However, if you add GPL Version 2 code 
* and therefore, elected the GPL Version 2 license, then the option applies 
* only if the new code is made subject to such option by the copyright 
* holder. 
*/ 

package com.sun.faces.renderkit; 

import com.sun.faces.util.FacesLogger; 
import java.security.SecureRandom; 
import java.util.Arrays; 
import java.util.logging.Level; 
import java.util.logging.Logger; 
import javax.crypto.Cipher; 
import javax.crypto.KeyGenerator; 
import javax.crypto.Mac; 
import javax.crypto.SecretKey; 
import javax.crypto.spec.IvParameterSpec; 
import javax.faces.FacesException; 

/** 
* <p>This utility class is to provide both encryption and 
* decryption <code>Ciphers</code> to <code>ResponseStateManager</code> 
* implementations wishing to provide encryption support.</p> 
* 
* <p>The algorithm used to encrypt byte array is AES with CBC.</p> 
* 
* <p>Original author Inderjeet Singh, J2EE Blue Prints Team. Modified to suit JSF 
* needs.</p> 
*/ 
public final class ByteArrayGuard { 


    // Log instance for this class 
    private static final Logger LOGGER = FacesLogger.RENDERKIT.getLogger(); 

    private static final int MAC_LENGTH = 32; 
    private static final int KEY_LENGTH = 128; 
    private static final int IV_LENGTH = 16; 

    private static final String KEY_ALGORITHM = "AES"; 
    private static final String CIPHER_CODE = "AES/CBC/PKCS5Padding"; 
    private static final String MAC_CODE = "HmacSHA256"; 
    private SecretKey sk; 

    // ------------------------------------------------------------ Constructors 

    public ByteArrayGuard() { 

     try { 
      setupKeyAndMac(); 
     } catch (Exception e) { 
      if (LOGGER.isLoggable(Level.SEVERE)) { 
       LOGGER.log(Level.SEVERE, 
          "Unexpected exception initializing encryption." 
          + " No encryption will be performed.", 
          e); 
      } 
      System.err.println("ERROR: Initializing Ciphers"); 
     } 
    } 

    // ---------------------------------------------------------- Public Methods  


    /** 
    * This method: 
    * Encrypts bytes using a cipher. 
    * Generates MAC for intialization vector of the cipher 
    * Generates MAC for encrypted data 
    * Returns a byte array consisting of the following concatenated together: 
    *  |MAC for cnrypted Data | MAC for Init Vector | Encrypted Data | 
    * @param bytes The byte array to be encrypted. 
    * @return the encrypted byte array. 
    */ 
    public byte[] encrypt(byte[] bytes) { 
     byte[] securedata = null; 
     try { 
      // Generate IV 
      SecureRandom rand = new SecureRandom(); 
      byte[] iv = new byte[16]; 
      rand.nextBytes(iv); 
      IvParameterSpec ivspec = new IvParameterSpec(iv); 
      Cipher encryptCipher = Cipher.getInstance(CIPHER_CODE); 
      encryptCipher.init(Cipher.ENCRYPT_MODE, sk, ivspec); 
      Mac encryptMac = Mac.getInstance(MAC_CODE); 
      encryptMac.init(sk); 
      encryptMac.update(iv); 
      // encrypt the plaintext 
      byte[] encdata = encryptCipher.doFinal(bytes); 
      byte[] macBytes = encryptMac.doFinal(encdata); 
      byte[] tmp = concatBytes(macBytes, iv); 
      securedata = concatBytes(tmp, encdata); 
     } catch (Exception e) { 
      if (LOGGER.isLoggable(Level.SEVERE)) { 
       LOGGER.log(Level.SEVERE, 
          "Unexpected exception initializing encryption." 
          + " No encryption will be performed.", 
          e); 
      } 
      return null; 
     } 
     return securedata; 
    } 

    /** 
    * This method decrypts the provided byte array. 
    * The decryption is only performed if the regenerated MAC 
    * is the same as the MAC for the received value. 
    * @param bytes Encrypted byte array to be decrypted. 
    * @return Decrypted byte array. 
    */ 
    public byte[] decrypt(byte[] bytes) { 
     try { 
      // Extract MAC 
      byte[] macBytes = new byte[MAC_LENGTH]; 
      System.arraycopy(bytes, 0, macBytes, 0, macBytes.length); 

      // Extract IV 
      byte[] iv = new byte[IV_LENGTH]; 
      System.arraycopy(bytes, macBytes.length, iv, 0, iv.length); 

      // Extract encrypted data 
      byte[] encdata = new byte[bytes.length - macBytes.length - iv.length]; 
      System.arraycopy(bytes, macBytes.length + iv.length, encdata, 0, encdata.length); 

      IvParameterSpec ivspec = new IvParameterSpec(iv); 
      Cipher decryptCipher = Cipher.getInstance(CIPHER_CODE); 
      decryptCipher.init(Cipher.DECRYPT_MODE, sk, ivspec); 

      // verify MAC by regenerating it and comparing it with the received value 
      Mac decryptMac = Mac.getInstance(MAC_CODE); 
      decryptMac.init(sk); 
      decryptMac.update(iv); 
      decryptMac.update(encdata); 
      byte[] macBytesCalculated = decryptMac.doFinal(); 
      if (Arrays.equals(macBytes, macBytesCalculated)) { 
       // continue only if the MAC was valid 
       // System.out.println("Valid MAC found!"); 
       byte[] plaindata = decryptCipher.doFinal(encdata); 
       return plaindata; 
      } else { 
       System.err.println("ERROR: MAC did not verify!"); 
       return null; 
      } 
     } catch (Exception e) { 
      System.err.println("ERROR: Decrypting:"+e.getCause()); 
      return null; // Signal to JSF runtime 
     } 
    } 

    // --------------------------------------------------------- Private Methods 

    /** 
    * Generates secret key. 
    * Initializes MAC(s). 
    */ 
    private void setupKeyAndMac() { 

     try { 
      KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM); 
      kg.init(KEY_LENGTH); // 256 if you're using the Unlimited Policy Files 
      sk = kg.generateKey(); 

     } catch (Exception e) { 
      throw new FacesException(e); 
     } 
    } 

    /** 
    * This method concatenates two byte arrays 
    * @return a byte array of array1||array2 
    * @param array1 first byte array to be concatenated 
    * @param array2 second byte array to be concatenated 
    */ 
    private static byte[] concatBytes(byte[] array1, byte[] array2) { 
     byte[] cBytes = new byte[array1.length + array2.length]; 
     try { 
      System.arraycopy(array1, 0, cBytes, 0, array1.length); 
      System.arraycopy(array2, 0, cBytes, array1.length, array2.length); 
     } catch(Exception e) { 
      throw new FacesException(e); 
     } 
     return cBytes; 
    }  
} 

并重新加载这个jar。 JSF_REFERENCE

+2

这不是一个解决方案。这是一个不负责任的解决方法。 – BalusC