Spring Security 自定义过滤器
Posted
技术标签:
【中文标题】Spring Security 自定义过滤器【英文标题】:Spring Security custom filter 【发布时间】:2011-08-23 17:00:18 【问题描述】:我想自定义 Spring security 3.0.5 并将登录 URL 更改为 /login 而不是 /j_spring_security_check。
我需要做的是允许登录到“/”目录并保护“/admin/report.html”页面。
首先我使用教程和 Spring Security 源代码创建了我自己的过滤器:
public class MyFilter extends AbstractAuthenticationProcessingFilter
private static final String DEFAULT_FILTER_PROCESSES_URL = "/login";
private static final String POST = "POST";
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password";
public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
protected MyFilter()
super(DEFAULT_FILTER_PROCESSES_URL);
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response)
throws AuthenticationException, IOException, ServletException
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null)
username = "";
if (password == null)
password = "";
username = username.trim();
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
HttpSession session = request.getSession(false);
if (session != null || getAllowSessionCreation())
request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY, TextEscapeUtils.escapeEntities(username));
setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest)
authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
@Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
if (request.getMethod().equals(POST))
// If the incoming request is a POST, then we send it up
// to the AbstractAuthenticationProcessingFilter.
super.doFilter(request, response, chain);
else
// If it's a GET, we ignore this request and send it
// to the next filter in the chain. In this case, that
// pretty much means the request will hit the /login
// controller which will process the request to show the
// login page.
chain.doFilter(request, response);
protected String obtainUsername(HttpServletRequest request)
return request.getParameter(usernameParameter);
protected String obtainPassword(HttpServletRequest request)
return request.getParameter(passwordParameter);
之后我在 xml 中进行以下更改
<security:http auto-config="true">
<!--<session-management session-fixation-protection="none"/>-->
<security:custom-filter ref="myFilter" before="FORM_LOGIN_FILTER"/>
<security:intercept-url pattern="/admin/login.jsp*" filters="none"/>
<security:intercept-url pattern="/admin/report.html" access="ROLE_ADMIN"/>
<security:form-login login-page="/admin/login.jsp" login-processing-url="/login" always-use-default-target="true"/>
<security:logout logout-url="/logout" logout-success-url="/login.jsp" invalidate-session="true"/>
</security:http>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:password-encoder hash="md5" />
<security:user-service>
<!-- peter/opal -->
<security:user name="peter" password="22b5c9accc6e1ba628cedc63a72d57f8" authorities="ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
<bean id="myFilter" class="com.vanilla.springMVC.controllers.MyFilter">
<property name="authenticationManager" ref="authenticationManager"/>
</bean>
然后我的代码中有 JSP。
<form action="../login" method="post">
<label for="j_username">Username</label>
<input type="text" name="j_username" id="j_username" />
<br/>
<label for="j_password">Password</label>
<input type="password" name="j_password" id="j_password"/>
<br/>
<input type='checkbox' name='_spring_security_remember_me'/> Remember me on this computer.
<br/>
<input type="submit" value="Login"/>
</form>
当我试图导航到 /admin/report.html 时,我被重定向到登录页面。 但在提交凭据后,我得到:
HTTP Status 404 - /SpringMVC/login/
type Status report
message /SpringMVC/login/
description The requested resource (/SpringMVC/login/) is not available.
看起来我的配置有问题,但我不知道是什么原因造成的。 你能帮忙吗?
【问题讨论】:
action="../login"
?
没错,../login 因为否则我会得到 /admin/login 并且我需要父文件夹。
您的应用程序的根是/SpringMVC
? /SpringMVC/login/
正确/应该可用?
是的,root是/SpringMVC/,然后是securet文件夹/SpringMVC/admin/report.html,然后是登录文件夹/SpringMVC/admin/login.jsp。
我相信我的问题出在 xml 文件中。但我可以弄清楚问题出在哪里,我感觉我的过滤器无法正常工作,但我知道如何让它正确。
【参考方案1】:
我在这方面晚了大约 12 个月,但是为了自定义 Spring Security 表单登录的登录 URL,您不需要创建自己的过滤器。 form-login 标记的属性之一允许您设置自定义 URL。实际上,您还可以使用 form-login 标记的属性更改默认的 j_username 和 j_password 字段名称。这是一个例子:
<form-login login-page="/login" login-processing-url="/login.do" default-target-url="/" always-use-default-target="true" authentication-failure-url="/login?error=1" username-parameter="username" password-parameter="password"/>
【讨论】:
【参考方案2】:我认为@Ischin 对表单操作 url 的疑惑是正确的。尝试输入完整路径,看看是否有效。如果是这样,您可以从那里找出不匹配的内容。
我能想到的唯一要检查的是 web.xml 中的过滤器映射。由于您正在点击登录页面,因此您已经设置了此设置,但我会检查您是否不仅拦截了具有特定扩展名等的 url。
另外,仅供参考,如果您希望请求(一旦登录表单验证用户身份)转到受保护的资源(在本例中为 /admin/report.html),那么您应该始终删除表单:登录-使用默认目标=“真”。将此标志设置为 true 将导致请求始终转到默认目标 url,这通常不是您想要的。来自spring security docs:
映射到 defaultTargetUrl 属性 的 用户名密码身份验证过滤器。 如果不设置,默认值为“/” (应用程序根)。用户将 登录后转到此 URL, 前提是他们没有被要求登录 在尝试访问安全的 资源,当他们将被带到 最初请求的 URL。
【讨论】:
有关否决票的更多信息,请参阅下面的答案以上是关于Spring Security 自定义过滤器的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spring Security 中编写自定义过滤器?
Spring Security 自定义过滤器被基本 http 安全覆盖