四Spring Security使用自定义认证页面
Posted 上善若水
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四Spring Security使用自定义认证页面相关的知识,希望对你有一定的参考价值。
一、 SpringSecurity使用自定义认证页面
1.1、在Spring Security主配置文件中指定认证页面配置信息
spring-security.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!--直接释放无需经过SpringSecurity过滤器的静态资源-->
<security:http pattern="/css/**" security="none"/>
<security:http pattern="/img/**" security="none"/>
<security:http pattern="/plugins/**" security="none"/>
<security:http pattern="/failer.jsp" security="none"/>
<security:http pattern="/favicon.ico" security="none"/>
<!--
设置可以用spring的el表达式配置Spring Security并自动生成对应配置组件(过滤器)
auto-config="true" 表示自动加载Spring Security的配置文件
use-expressions="true" 表示使用Spring的el表达式来配置Spring Security
-->
<security:http auto-config="true" use-expressions="true">
<!-- 指定login.jsp页面可以被匿名访问 -->
<security:intercept-url pattern="/login.jsp" access="permitAll()"/>
<!--
使用spring的el表达式来指定项目所有资源访问都必须有ROLE_USER或ROLE_ADMIN角色
pattern="/**" 表示拦截所有资源
access="hasAnyRole('ROLE_USER')" 表示只有ROLE_USER角色才能访问资源
-->
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
<!--指定自定义的认证页面-->
<security:form-login login-page="/login.jsp" login-processing-url="/login"
default-target-url="/index.jsp" authentication-failure-url="/failer.jsp"/>
<!--指定退出登录后跳转的页面-->
<security:logout logout-url="/logout" logout-success-url="/login.jsp"/>
</security:http>
<!--设置Spring Security认证用户信息的来源-->
<!--Spring Security默认的认证必须是加密的,加上{noop}表示不加密认证-->
<security:authentication-manager>
<security:authentication-provider>
<security:user-service>
<security:user name="user" password="{noop}user"
authorities="ROLE_USER" />
<security:user name="admin" password="{noop}admin"
authorities="ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
</beans>
修改认证页面的请求地址
再次启动项目后就可以看到自定义的酷炫认证页面了!
然后你开开心心的输入了用户名 user,密码user,就出现了如下的界面:
403 什么异常?这是SpringSecurity中的权限不足!这个异常怎么来的?还记得上面SpringSecurity内置认证页面源码中的那个_csrf隐藏input吗?问题就在这了!
1.2、SpringSecurity 的csrf防护机制
CSRF(Cross-site request forgery)跨站请求伪造,是一种难以防范的网络攻击方式。
1.2.1、SpringSecurity中CsrfFilter过滤器说明
package org.springframework.security.web.csrf;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.access.AccessDeniedHandlerImpl;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;
public final class CsrfFilter extends OncePerRequestFilter {
public static final RequestMatcher DEFAULT_CSRF_MATCHER = new CsrfFilter.DefaultRequiresCsrfMatcher();
private final Log logger = LogFactory.getLog(this.getClass());
private final CsrfTokenRepository tokenRepository;
private RequestMatcher requireCsrfProtectionMatcher;
private AccessDeniedHandler accessDeniedHandler;
public CsrfFilter(CsrfTokenRepository csrfTokenRepository) {
this.requireCsrfProtectionMatcher = DEFAULT_CSRF_MATCHER;
this.accessDeniedHandler = new AccessDeniedHandlerImpl();
Assert.notNull(csrfTokenRepository, "csrfTokenRepository cannot be null");
this.tokenRepository = csrfTokenRepository;
}
//通过这里可以看出SpringSecurity的csrf机制把请求方式分成两类来处理
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
request.setAttribute(HttpServletResponse.class.getName(), response);
CsrfToken csrfToken = this.tokenRepository.loadToken(request);
boolean missingToken = csrfToken == null;
if (missingToken) {
csrfToken = this.tokenRepository.generateToken(request);
this.tokenRepository.saveToken(csrfToken, request, response);
}
request.setAttribute(CsrfToken.class.getName(), csrfToken);
request.setAttribute(csrfToken.getParameterName(), csrfToken);
//第一类:"GET", "HEAD", "TRACE", "OPTIONS"四类请求可以直接通过
if (!this.requireCsrfProtectionMatcher.matches(request)) {
filterChain.doFilter(request, response);
} else {
//第二类:除去上面四类,包括POST都要被验证携带token才能通过
String actualToken = request.getHeader(csrfToken.getHeaderName());
if (actualToken == null) {
actualToken = request.getParameter(csrfToken.getParameterName());
}
if (!csrfToken.getToken().equals(actualToken)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request));
}
if (missingToken) {
this.accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken));
} else {
this.accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken));
}
} else {
filterChain.doFilter(request, response);
}
}
}
public void setRequireCsrfProtectionMatcher(RequestMatcher requireCsrfProtectionMatcher) {
Assert.notNull(requireCsrfProtectionMatcher, "requireCsrfProtectionMatcher cannot be null");
this.requireCsrfProtectionMatcher = requireCsrfProtectionMatcher;
}
public void setAccessDeniedHandler(AccessDeniedHandler accessDeniedHandler) {
Assert.notNull(accessDeniedHandler, "accessDeniedHandler cannot be null");
this.accessDeniedHandler = accessDeniedHandler;
}
private static final class DefaultRequiresCsrfMatcher implements RequestMatcher {
private final HashSet<String> allowedMethods;
private DefaultRequiresCsrfMatcher() {
this.allowedMethods = new HashSet(Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS"));
}
public boolean matches(HttpServletRequest request) {
return !this.allowedMethods.contains(request.getMethod());
}
}
}
通过源码分析,我们明白了,自己的认证页面,请求方式为POST,但却没有携带token,所以才出现了403权限不
足的异常。那么如何处理这个问题呢?
方式一:直接禁用csrf,不推荐。
方式二:在认证页面携带token请求。
1.2.2、禁用csrf防护机制(不推荐)
在SpringSecurity主配置文件中添加禁用crsf防护的配置。
<!--
设置可以用spring的el表达式配置Spring Security并自动生成对应配置组件(过滤器)
auto-config="true" 表示自动加载Spring Security的配置文件
use-expressions="true" 表示使用Spring的el表达式来配置Spring Security
-->
<security:http auto-config="true" use-expressions="true">
<!-- 指定login.jsp页面可以被匿名访问 -->
<security:intercept-url pattern="/login.jsp" access="permitAll()"/>
<!--
使用spring的el表达式来指定项目所有资源访问都必须有ROLE_USER或ROLE_ADMIN角色
pattern="/**" 表示拦截所有资源
access="hasAnyRole('ROLE_USER')" 表示只有ROLE_USER角色才能访问资源
-->
<security:intercept-url pattern="/**" access="hasAnyRole('ROLE_USER')"/>
<!--指定自定义的认证页面-->
<security:form-login login-page="/login.jsp" login-processing-url="/login"
default-target-url="/index.jsp" authentication-failure-url="/failer.jsp"/>
<!--指定退出登录后跳转的页面-->
<security:logout logout-url="/logout" logout-success-url="/login.jsp"/>
<!--禁用csrf防护机制-->
<security:csrf disabled="true"/>
</security:http>
1.2.3、 在认证页面携带token请求
注: HttpSessionCsrfTokenRepository对象负责生成token并放入session域中。
1.2.4、运行
以上是关于四Spring Security使用自定义认证页面的主要内容,如果未能解决你的问题,请参考以下文章