最近碰到一个需求,就是在原有的spring mvc框架上添加登录验证的功能,有两种实现方式:
1、Spring security
在原有的spring框架上,配置Spring security的拦截器,web.xml配置如下:
<!-- 通过全局参数来引入配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-security.xml</param-value>
</context-param>
<!-- 对静态资源的配置 -->
<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 配置如下:
<!-- 页面放行,不拦截-->
<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">
<!-- 配置拦截的请求,任何请求地址都必须有ROLE_USER的权限-->
<security:intercept-url pattern="/**" access="ROLE_USER"/>
<!-- login-page:指定登陆页面
login-processing-url:登陆请求路径,登陆时必须使用该路径
default-target-url:登陆成功后进入的页面-->
<security:form-login login-page="/login/showLogin"
login-processing-url="/login"
default-target-url="/login/userList"
authentication-failure-url="/warehouse/main"
></security:form-login>
<!-- 关闭跨站请求伪造-->
<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里配置拦截器,就不贴出来了。
优点:
spring security可实现加密认证等,默认的登录认证是通过提交表单实现的,可根据实际需求改为js提交认证,如下:
<button style="width:200px;height:100px;font-size:1.5em" onclick="sub();return false;" >登 录</button>
用自定义按钮替代“submit”按钮,且需要return false,否则表单会自动提交,在完成逻辑认证后,可用$("#XX").submit();实现表单提交,“XX”是form表单的id。
缺点:
Spring security登录验证必须通过连接数据库做校验,假设项目要求数据库不在本系统管辖,即无法直连数据库,则该方式不适合登录验证,就需要用下面的方式——token认证。
2、token认证
俗称登录票,即用户登录时需要通过token来进行校验,一般放到http的header里,常用的加密方式有jwt加密。但在实际应用过程中发现了一个问题,就是前端跳转页面的时候不会把token带到后台,跳转的时候用了几种方式:1、ajax,ajax可以将token添加到header里,但是必须得有返回值,所以还是得在返回成功后在前端跳转;2、window.location.href,该方法跳转的时候会自动进入到后端的controller里,然后通过controller实现跳转,但无法携带token。
尝试过不通过controller(即html或jsp文件不放在WEB-INF下),直接跳转,一样无法携带token,故推测应该是框架的问题。最后选择了个折衷的办法,在前端实现验证,在登录完成后,会将token存在一个全局的地方,如localStorage;在每次访问一个新页面的时候,就会检测是否登录,即检测token,没有的话,就跳转到登录页面。思路参考于由前端登录验证,页面跳转,携带headers token引发的思考和尝试 - southday - 博客园。
简单的说,就是封装一个js,每次刷新页面的时候调用该请求,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)
}
/**
* 清空localStorage
*/
function clearStorage() {
localStorage.clear();
}
jsp页面引用时,只需如下添加即可:
<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>
这样可以实现访问新页面时,如果没有登录则自动跳转到登录页面,但细想其实还存在一个问题,就是前端无法实现token的过期验证,目前还没有较好的办法,如果有后续会更新上来,所以如果还想从根源上解决问题,还是需要选择一个合适的框架,前端跳转的时候自带有路由功能。