若有不足,敬請各位提出批評,定會改正。THX!javascript
本文介紹的是RSA加密算法+Spring Security在SpringMVC中的集成使用。css
Spring Security是什麼? html
學習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 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服務器中,不然每次你刪除項目的時候,或者從新部署都會形成密鑰對文件的從新生成。或者每次都用舊的替換新生成的,保持可用性。
二、前臺請求服務器的公鑰,傳輸到前臺,由於公鑰是公開的,使用公鑰在前臺加密用戶登陸註冊或者其餘保密的信息,將公鑰加密的密文傳輸到服務器。服務器經過與公鑰對應的私鑰,解密數據庫的密文和傳輸過來的密文,將解密後的字符相比較,再將確認信息返回到前臺,進行下一步的操做。