最近碰到一個(gè)需求,就是在原有的spring mvc框架上添加登錄驗(yàn)證的功能,有兩種實(shí)現(xiàn)方式:
1、Spring security
????? 在原有的spring框架上,配置Spring security的攔截器,web.xml配置如下:
<!-- 通過全局參數(shù)來(lái)引入配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-security.xml</param-value>
</context-param>
<!-- 對(duì)靜態(tài)資源的配置 -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>...
<!--委派代理過濾器-->
<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 security 配置如下:
<!-- 頁(yè)面放行,不攔截-->
<security:http pattern="/login/showLogin" security="none"></security:http>
<security:http pattern="/scripts/**" security="none"></security:http>
...
<security:http auto-config="true" use-expressions="false">
<!-- 配置攔截的請(qǐng)求,任何請(qǐng)求地址都必須有ROLE_USER的權(quán)限-->
? <security:intercept-url pattern="/**" access="ROLE_USER"/>
<!-- login-page:指定登陸頁(yè)面
??????? login-processing-url:登陸請(qǐng)求路徑,登陸時(shí)必須使用該路徑
??????? default-target-url:登陸成功后進(jìn)入的頁(yè)面-->
? <security:form-login login-page="/login/showLogin"
? ? ? ? ? ? ? ? ? login-processing-url="/login"
? ? ? ? ? ? ? ? ? default-target-url="/login/userList"
? ? ? ? ? ? ? ? ? authentication-failure-url="/warehouse/main"
? ></security:form-login>
<!-- 關(guān)閉跨站請(qǐng)求偽造-->
? <security:csrf disabled="true"></security:csrf>
<!-- 退出-->
? <security:logout invalidate-session="true" logout-url="/logout" logout-success-url="/login/showLogin"></security:logout>
</security:http>
???? mvc.xml里配置攔截器,就不貼出來(lái)了。
優(yōu)點(diǎn):
???? spring security可實(shí)現(xiàn)加密認(rèn)證等,默認(rèn)的登錄認(rèn)證是通過提交表單實(shí)現(xiàn)的,可根據(jù)實(shí)際需求改為js提交認(rèn)證,如下:
<button style="width:200px;height:100px;font-size:1.5em" onclick="sub();return false;" >登 錄</button>
? ? ? ??? 用自定義按鈕替代“submit”按鈕,且需要return false,否則表單會(huì)自動(dòng)提交,在完成邏輯認(rèn)證后,可用$("#XX").submit();實(shí)現(xiàn)表單提交,“XX”是form表單的id。
缺點(diǎn):
?????? Spring security登錄驗(yàn)證必須通過連接數(shù)據(jù)庫(kù)做校驗(yàn),假設(shè)項(xiàng)目要求數(shù)據(jù)庫(kù)不在本系統(tǒng)管轄,即無(wú)法直連數(shù)據(jù)庫(kù),則該方式不適合登錄驗(yàn)證,就需要用下面的方式——token認(rèn)證。
2、token認(rèn)證
??? 俗稱登錄票,即用戶登錄時(shí)需要通過token來(lái)進(jìn)行校驗(yàn),一般放到http的header里,常用的加密方式有jwt加密。但在實(shí)際應(yīng)用過程中發(fā)現(xiàn)了一個(gè)問題,就是前端跳轉(zhuǎn)頁(yè)面的時(shí)候不會(huì)把token帶到后臺(tái),跳轉(zhuǎn)的時(shí)候用了幾種方式:1、ajax,ajax可以將token添加到header里,但是必須得有返回值,所以還是得在返回成功后在前端跳轉(zhuǎn);2、window.location.href,該方法跳轉(zhuǎn)的時(shí)候會(huì)自動(dòng)進(jìn)入到后端的controller里,然后通過controller實(shí)現(xiàn)跳轉(zhuǎn),但無(wú)法攜帶token。
??? 嘗試過不通過controller(即html或jsp文件不放在WEB-INF下),直接跳轉(zhuǎn),一樣無(wú)法攜帶token,故推測(cè)應(yīng)該是框架的問題。最后選擇了個(gè)折衷的辦法,在前端實(shí)現(xiàn)驗(yàn)證,在登錄完成后,會(huì)將token存在一個(gè)全局的地方,如localStorage;在每次訪問一個(gè)新頁(yè)面的時(shí)候,就會(huì)檢測(cè)是否登錄,即檢測(cè)token,沒有的話,就跳轉(zhuǎn)到登錄頁(yè)面。思路參考于由前端登錄驗(yàn)證,頁(yè)面跳轉(zhuǎn),攜帶headers token引發(fā)的思考和嘗試 - southday - 博客園。
??? 簡(jiǎn)單的說(shuō),就是封裝一個(gè)js,每次刷新頁(yè)面的時(shí)候調(diào)用該請(qǐng)求,admin-index.js如下:
$(function() {
axios({
???? method:'post',
???? url:'/login/info',
???? headers: {'token':getAdminToken()}
}).then(function(resp) {
???? if (!resp.data){
???????? window.location.href ="/login/showLogin";
????? }
? ? }).catch(function(error) {
????? console.log(error)
})
})
??? common.js如下:
/**
* 從localStorage中獲取adminToken
* @returns {string}
*/
function getAdminToken() {
??? return localStorage.getItem("adminToken")
}
/**
* 將adminToken保存到localStorage中
* @param token
*/
function saveAdminToken(token) {
??? localStorage.setItem("adminToken", token)
}
/**
* 將admin保存到localStorage
* @param admin
*/
function saveAdmin(admin) {
???? localStorage.setItem("admin", ($.isEmptyObject(admin) ?null :JSON.stringify(admin)))
}
/**
* 從localStorage中取user
* @returns {admin}
*/
function getAdmin() {
???? var a =localStorage.getItem("admin")
??? return $.isEmptyObject(a) ?null :JSON.parse(a)
}
/**
* 清空l(shuí)ocalStorage
*/
function clearStorage() {
???? localStorage.clear();
}
??? jsp頁(yè)面引用時(shí),只需如下添加即可:
<script type="text/javascript" src="<c:url value="/scripts/axios.min.js"/>" ></script>
<script type="text/javascript" src="<c:url value="/scripts/admin-index.js"/>" ></script>
<script type="text/javascript" src="<c:url value="/scripts/common.js"/>" ></script>
?????? 這樣可以實(shí)現(xiàn)訪問新頁(yè)面時(shí),如果沒有登錄則自動(dòng)跳轉(zhuǎn)到登錄頁(yè)面,但細(xì)想其實(shí)還存在一個(gè)問題,就是前端無(wú)法實(shí)現(xiàn)token的過期驗(yàn)證,目前還沒有較好的辦法,如果有后續(xù)會(huì)更新上來(lái),所以如果還想從根源上解決問題,還是需要選擇一個(gè)合適的框架,前端跳轉(zhuǎn)的時(shí)候自帶有路由功能。