是否可以在 Spring 中以基于表单的身份验证发送更多数据?
-
06-07-2019 - |
题
我对这个比较陌生 Spring框架 和春季安全。
我使用了自定义身份验证方案 HTML:
<form action="j_spring_security_check">
<input type="text" name="j_username" value="abc"/>
<input type="text" name="j_password" value="abc"/>
<input type="text" name="myCustom1" value="pqr"/> <!-- maybe type="hidden" -->
<input type="text" name="myCustom2" value="pqr"/> <!-- maybe type="hidden" -->
</form>
以及相应的代码:
public class CustomAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider
{
@Override protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken)
throws AuthenticationException
{
System.out.println("Method invoked : additionalAuthenticationChecks isAuthenticated ? :"+usernamePasswordAuthenticationToken.isAuthenticated());
}
@Override protected UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException
{
System.out.println("Method invoked : retrieveUser");
//I have Username,password:
//HOW CAN I ACCESS "myCustom1", "myCustom2" here ?
}
}
解决方案
如果您需要使用其他表单参数来操作用户名和密码,您可以实现自己的AuthenticationProcessingFilter
此类将具有对HttpRequest的完全访问权限,因此您将提交所有其他参数。如果您的目标是以某种方式使用这些值来修改用户名和密码,那么您可以在此处执行此操作。
其他提示
以上都是伟大而完美的解决方案。 但我使用了一种解决方法,它完美地运行。 已使用ThreadLocal的多租户ID
package com.mypackage.servlet;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.util.Assert;
public class ThreadLocalContextUtil implements Filter{
private static final ThreadLocal<Object> contextHolder =
new ThreadLocal<Object>();
public static void setTenantId(Object tenantId) {
Assert.notNull(tenantId, "customerType cannot be null");
contextHolder.set(tenantId);
}
public static Object getTenantId() {
return contextHolder.get();
}
public static void clearTenant() {
contextHolder.remove();
}
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// Set the tenant Id into a ThreadLocal object
ThreadLocalContextUtil.setTenantId(request);
if(chain != null)
chain.doFilter(request, response);
else {
//error
}
}
public void init(FilterConfig filterconfig) throws ServletException {
}
}
spring security xml
<security:http auto-config="true" use-expressions="true" access-denied-page="/forms/auth/403" >
<security:custom-filter before="FIRST" ref="tenantFilter" />
......
</security:http>
您的身份验证类
中的访问请求对象HttpServletRequest currRequest = (HttpServletRequest) ThreadLocalContextUtil.getTenantId();
然后使用请求对象获取自定义参数
这里的技巧是,您需要创建一个新的 AuthenicationToken (可能)扩展 UsernameAndPasswordAuthenicationToken ,正如 @emills 所说,您需要实现一个新的 AuthenciationProcessingFilter 将请求值映射到令牌并将这些值提交到 AuthenicationManager 。
基本上,在 spring-security 中实现自定义身份验证链有几个部分
AuthenicationToken
- 身份验证请求及其结果的详细信息,即包含您进行身份验证所需的凭据AuthenicationProvider
- 向 AuthenicationManager 注册,接受您的 AuthenicationToken 并验证用户并返回具有授予权限集的令牌AuthenciationFilter
- 实际上不必是一个过滤器,只需使用 AbstractProcessingFilter 就会让你的生活变得更轻松
我会这样:
<bean id="authenticationProcessingFilter"
class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter">
...
<property name="authenticationDetailsSource">
<bean class="org.acegisecurity.ui.AuthenticationDetailsSourceImpl">
<property name="clazz"
value="com.MyAuthenticationDetails"/>
</bean>
</property>
</bean>
这是包含属性的类:
package com;
import javax.servlet.http.HttpServletRequest;
import org.acegisecurity.ui.WebAuthenticationDetails;
public class MyAuthenticationDetails extends WebAuthenticationDetails {
public MyAuthenticationDetails() {
super();
}
//This constructor will be invoqued by the filter
public MyAuthenticationDetails(HttpServletRequest request) {
super(request);
this.myCustom1 = request.getParameter("myCustom1");
}
public String getMyCustom1() {
return myCustom1;
}
private String myCustom1;
}
现在您拥有用户名,密码和详细信息。
我做了类似的事情,但不同于任何人在这里提出的建议。我不是说这是“正确的”这样做的方式 - 但它对我来说非常好。在Principal对象中有用户,AuthenticationToken中还有一个Details对象,您可以存储其他登录信息的Map(String,String)。
public class RequestFormDeatils extends SpringSecurityFilter {
protected void doFilterHttp(HttpServletRequest request, ...) {
SecurityContext sec = SecurityContextHolder.getContent();
AbstractAuthenticationToken auth = (AbstractAuthenticationToken)sec.getAuthentication();
Map<String, String> m = new HashMap<String, String>;
m.put("myCustom1", request.getParamtere("myCustom1"));
m.put("myCustom2", request.getParameter("myCustom2"));
auth.setDetails(m);
}
现在,您的代码中的任何位置都可以使用SecurityContext传播此安全相关信息,而无需将其耦合到UserDetails对象,或将其作为参数传递。我在Spring Security Filter链的末尾的SecurityFilter中执行此代码。
<bean id="requestFormFilter" class="...RequestFormDetails">
<custom-filter position="LAST" />
</bean>