RSA加密算法及其與SpringMVC集成

若有不足,敬請各位提出批評,定會改正。THX!javascript

 

本文介紹的是RSA加密算法+Spring Security在SpringMVC中的集成使用。css

Spring Security是什麼? html

引用:
  Spring Security是一個可以爲基於Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組能夠在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Control ,DI:Dependency Injection 依賴注入)和AOP(面向切面編程)功能,爲應用系統提供聲明式的安全訪問控制功能,減小了爲企業系統安全控制編寫大量重複代碼的工做。
  Spring Security之前叫作acegi,是後來才成爲Spring的一個子項目,也是目前最爲流行的一個安全權限管理框架,它與Spring緊密結合在一塊兒。
  Spring Security關注的重點是在企業應用安全層爲您提供服務,你將發現業務問題領域存在着各式各樣的需求。銀行系統跟電子商務應用就有很大的不一樣。電子商務系統與企業銷售自動化工具又有很大不一樣。這些客戶化需求讓應用安全顯得有趣,富有挑戰性並且物有所值。Spring Security爲基於J2EE的企業應用軟件提供了一套全面的安全解決方案。

學習Spring Security的網址http://www.iteye.com/blogs/subjects/spingsecurity3inside。java

Spring-Security 自帶的加密算法有web

- bcrypt
- plaintext
- sha
- sha-256
- md5
- md4
- {sha}
- {ssha}算法

 因爲md5加密算法,使用較爲廣泛,可是能夠經過碰撞的方式破解,不採用。spring

 RSA是什麼?      數據庫

      同RSA(Ron Rivest,Adi Shamir,Len Adleman三位天才的名字)同樣,ECC(Elliptic Curves Cryptography,橢圓曲線密碼編碼學)也屬於公開密鑰算法。可是ECC算法在jdk1.5後加入支持,目前僅僅只能完成密鑰的生成與解析。express

  RSA是目前最有影響力的公鑰加密算法,它可以抵抗到目前爲止已知的絕大多數密碼攻擊,已被ISO推薦爲公鑰數據加密標準。今天只有短的RSA鑰匙纔可能被強力方式解破。到2008年爲止,世界上尚未任何可靠的攻擊RSA算法的方式。只要其鑰匙的長度足夠長,用RSA加密的信息其實是不能被解破的。但在分佈式計算和量子計算機理論日趨成熟的今天,RSA加密安全性受到了挑戰。RSA算法基於一個十分簡單的數論事實:將兩個大素數相乘十分容易,可是想要對其乘積進行因式分解卻極其困難,所以能夠將乘積公開做爲加密密鑰。apache

RSA密鑰長度隨着保密級別提升,增長很快。下表列出了對同一安全級別所對應的密鑰長度。

保密級別
對稱密鑰長度(bit)
RSA密鑰長度(bit)
ECC密鑰長度(bit)
保密年限
80
80
1024
160
2010
112
112
2048
224
2030
128
128
3072
256
2040
192
192
7680
384
2080
256
256
15360
512
2120

 

如何實現的RSA+Spring Security 在Spring MVC中的實現,直接上web.xml

 xml中的com.user.sec. MyHttpSessionEventPublisher
爲自定義的org.springframework.security.web.session.HttpSessionEventPublisher  二者使用其一。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:web="http://java.sun.com/xml/ns/javaee" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" metadata-complete="false" version="2.5">
 <!--配置上下文參數,指定spring配置文件的位置 -->  
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/applicationContext-*.xml</param-value> </context-param>
<!--Spring Security 過濾器-->
<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> </filter-mapping>
<!--Spring 字符集過濾器 -->
<!--包含設置兩個參數encoding和forceEncoding--> <filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>*</url-pattern> </filter-mapping> <filter> <filter-name>hibernateFilter</filter-name> <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class> <init-param> <param-name>excludeSuffixs</param-name> <param-value>js,css,jpg,gif</param-value> </init-param> </filter> <filter-mapping> <filter-name>hibernateFilter</filter-name> <url-pattern>*</url-pattern> </filter-mapping> <listener> <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class> </listener> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class>com.user.sec.MyHttpSessionEventPublisher</listener-class> </listener> <!-- <listener> <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class> </listener> --> <listener> <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class> </listener> <context-param> <param-name>log4jConfigLocation</param-name> <param-value>WEB-INF/classes/properties/log4j.properties</param-value> </context-param> <context-param> <param-name>log4jRefreshInterval</param-name> <param-value>60000</param-value> </context-param> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath*:spring/applicationContext-springmvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <servlet-name>springmvc</servlet-name> </filter-mapping> <session-config> <session-timeout>30</session-timeout> </session-config> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <error-page> <error-code>500</error-code> <location>/500.html</location> </error-page> </web-app>

applicationContext-security.xml

  Spring Security採用就近原則,有多個約束時,從上至下只要找到第一條知足就返回,所以因該將最嚴格的約束放在最前面,而將最寬鬆的約束放在最後面.auto-config屬性可讓spring security爲咱們自動配置幾種經常使用的權限控制機制,包括form,anonymous, rememberMe等。固然你也能夠手工配置。例如:<http auto-config="true">  

對於攔截pattern的設置,具體以下:

/  全部帶/的請求

/* 表明這個域下面的請求  例如:/user/xxx 這種會被攔截  可是不會攔截 /user/xxx/xxx

/** 表明跨域請求  例如:/user/xxx  和 /user/xxx/xxx都會被攔截

<?xml version="1.0" encoding="UTF-8"?>

<beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd">

    <global-method-security secured-annotations="enabled" />
  
<!--配置不過濾的資源,包括登錄請求,靜態資源訪問,註冊頁面,獲取登錄註冊驗證碼,固然也包括此篇文章所涉及從後臺獲取的公鑰接口-->
   <http pattern="/resources/**" security="none" /> <http pattern="/login.html" security="none" /> <http pattern="/register.html" security="none" /> <http pattern="/admin/user/getPairKey" security="none"
<http use-expressions="true" auto-config="true"> <!--容許登陸用戶操做 --> <intercept-url pattern="/pages/**" access="permitAll" /> <intercept-url pattern="/**" access="isAuthenticated()" /> <!--全部用戶都可使用 --> <!-- <intercept-url pattern="/**" access="permitAll" /> --> <!-- 容許匿名用戶訪問 --> <!-- <intercept-url pattern="/" access="IS_AUTHENTICATED_ANONYMOUSLY" /> --> <!-- <intercept-url pattern="/" access="ROLE_ANONYMOUS" /> --> <!-- 驗證失敗頁面 --> <form-login login-page="/login.html" authentication-failure-url="/login.html?error=true" login-processing-url="/j_spring_security_check" default-target-url="/index.html" always-use-default-target="true" /> <!-- 註銷頁面 --> <logout logout-success-url="/login.html" /> <!-- 記住登錄 --> <remember-me key="mpbuser" user-service-ref="userDetailService" /> <session-management invalid-session-url="/login.html" session-authentication-error-url="/login.html"> <concurrency-control max-sessions="100" error-if-maximum-exceeded="false" expired-url="/login.html" /> </session-management> <custom-filter before="FILTER_SECURITY_INTERCEPTOR" ref="myDefineFilter" /> <!-- 驗證權限不足處理類 --> <access-denied-handler ref="accessDeniedHandler" /> <http-basic /> </http> <beans:bean id="encoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder" /> <beans:bean id="RSAEncoder" class="com.common.component.util.RSAPasswordEncoder" /> <authentication-manager alias="authenticationManager" erase-credentials="false"> <authentication-provider user-service-ref="userDetailService"> <password-encoder ref="RSAEncoder" /> </authentication-provider> <!--自定義Provider --> <!-- <authentication-provider ref="myAuthenticationProvider"> </authentication-provider> --> </authentication-manager> <beans:bean id="myDefineFilter" class="com.user.sec.MyFilterSecurityInterceptor"> <beans:property name="authenticationManager" ref="authenticationManager" /> <!-- <beans:property name="accessDecisionManager" ref="accessDecisionManager" /> --> <beans:property name="securityMetadataSource" ref="databaseDefinitionSource" /> <beans:property name="accessDecisionManager" ref="myAccessDecisionManager" /> </beans:bean> <!-- <beans:bean id="myAuthenticationProvider" class="crowdfunding.user.sec.MyAuthenticationProvider"/> --> <beans:bean id="databaseDefinitionSource" class="com.user.sec.DefinitionSourceFactoryBean"> <beans:constructor-arg ref="resourceDetailService" /> <!-- <beans:constructor-arg ref="userDetailService" /> --> </beans:bean> <beans:bean id="myAccessDecisionManager" class="com.user.sec.MyAccessDecisionManager"> </beans:bean> <beans:bean id="accessDeniedHandler" class="com.user.sec.MyAccessDeniedHandler" /> </beans:beans>

 RSAPasswordEncoder.Java

package com.common.component.util;

import java.net.URLDecoder;

import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.encoding.PasswordEncoder;

import Decoder.BASE64Decoder;
import Decoder.BASE64Encoder;
import com.common.component.util.MpbSecureRSAUtil;

/*******************************************************
 * Description:自定義RSA處理類 
 *                     如需增長Spring Security 另外的加密方式,從新定義此類<br/>
 ********************************************************/
public class RSAPasswordEncoder implements PasswordEncoder {

    /*****************************************
     * Description:明文加密 <br/>
     * @return
     * @param rawPass
     *            密碼
     ******************************************/
    public String encodePassword(String rawPass, Object salt) {

        String encoder = "";
        try {
            encoder = MpbSecureRSAUtil.decryptString(rawPass);
        } catch (Exception e) {

            e.printStackTrace();
        }
        return encoder;
    }

    /*****************************************
     * Description:驗證密碼是否有效主要是此方法的使用<br/>
     * 
     * @return boolean
     ******************************************/
    public boolean isPasswordValid(String encPass, String rawPass, Object salt) {

        if ( !MpbStringUtil.isNotBlank(encPass) || encPass.length() == 0) {
            return false;
        }
        String decPsw = MpbSecureRSAUtil.decryptString(encPass);
        String inpdecPsw = MpbSecureRSAUtil.decryptStringByJs(rawPass);
        try {
            decPsw = URLDecoder.decode(decPsw, "UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
            return  false;
        }
        return inpdecPsw.equals(decPsw);
    }

    /*****************************************
     * Description:私鑰解密 <br/>
     * 
     * @return
     ******************************************/
    public String decryptPassword(String rawPass, Object salt) {

        String encoder = "";
        try {
            encoder = MpbSecureRSAUtil.decryptString(rawPass);
        } catch (Exception e) {

            e.printStackTrace();
        }
        return encoder;
    }

    /*****************************************
     * Description:Base64解密 <br/>
     * @param key
     * @return
     * @throws Exception
     ******************************************/
    public static byte[] decoderBase64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }

    /*****************************************
     * Description:Base64加密 <br/>
     * 
     * @param key
     * @return
     * @throws Exception
     ******************************************/
    public static String encoderBase64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

}

 login.html

<script type="text/javascript" src="resources/js/RSA/security.js"></script>

 

    function mysubmit() {
         if ($("input[name='_spring_security_remember_me']").attr("checked") == "checked") {
            //若是記住用戶名和密碼框被選中則執行remenber()方法
            remember();
        }else{
            //若是未選中則清空cookie
            $.mpbSetCookie("EBS_ISCHECKED", "");
        } 
//從後臺獲取公鑰並加密,將加密後的密文傳輸到服務器處理
        $.mpbPost(
                "/admin/user/getPairKey", 
                {
                    "_method" : "GET"
                }, 
                function(data) {
        
                    var modulus = data['modul'];
                    var exponent = data['exponent'];
                    var key = RSAUtils.getKeyPair(exponent, '', modulus);
                    var pwd = $("#psw").val();
                    pwd = encodeURIComponent(pwd);
                    pwd = RSAUtils.encryptedString(key, pwd);
                    //$("#psw").attr("value",pwd);
                    $("input[name='j_password']").val(pwd);
                    $("#myform").submit();
                });
        
        
    }

Controler.Java

@ResponseBody
    @RequestMapping(value = "/getPairKey", method = RequestMethod.GET)
    public Map<?,?> getKey() throws Exception {
        RSAPublicKey publicKey = MpbSecureRSAUtil.getDefaultPublicKey();

        Map<String, String> key = new HashMap<String,String>();

        key.put("modul",
                new String(Hex.encodeHex(publicKey.getModulus().toByteArray())));
        key.put("exponent",
                new String(Hex.encodeHex(publicKey.getPublicExponent()
                        .toByteArray())));

        return key;
    }

 文件地址:

http://files.cnblogs.com/files/Sonet-life/security.js

http://files.cnblogs.com/files/Sonet-life/MpbSecureRSAUtil.rar

整個加密的思路:

一、項目在啓動時就檢查是否生成密鑰對文件,沒有就強制生成新的,記住利用RSA註冊的用戶在數據庫保存的是加密後的密文,RSA密鑰對文件要保證一致性,若是密鑰對文件生成了新的,並且你沒備份以前的密鑰對文件,將會形成不可挽回的後果。最好的方法,經過對MpbSecureRSAUtil中密鑰對文件的生成路徑設置成硬盤下的,不要放到Tomcat服務器中,不然每次你刪除項目的時候,或者從新部署都會形成密鑰對文件的從新生成。或者每次都用舊的替換新生成的,保持可用性。

二、前臺請求服務器的公鑰,傳輸到前臺,由於公鑰是公開的,使用公鑰在前臺加密用戶登陸註冊或者其餘保密的信息,將公鑰加密的密文傳輸到服務器。服務器經過與公鑰對應的私鑰,解密數據庫的密文和傳輸過來的密文,將解密後的字符相比較,再將確認信息返回到前臺,進行下一步的操做。

相關文章
相關標籤/搜索