如何使用 Spring Security 登录页面传递附加参数

Posted

技术标签:

【中文标题】如何使用 Spring Security 登录页面传递附加参数【英文标题】:How to pass an additional parameter with spring security login page 【发布时间】:2012-04-21 21:22:59 【问题描述】:

我正在尝试将数据库名称设置为来自 Spring Security 登录页面的请求输入参数。目前我只获得使用 spring security SecurityContextHolder.getContext().getAuthentication() 检索到的用户名。

如何访问登录页面上设置的附加字段?

【问题讨论】:

是登录前传这个参数还是登录后从DB获取参数? 我想在连接到数据库之前获取请求参数值,我想根据登录屏幕上提供的选择值来决定用户需要连接哪个数据库。 我不认为,Spring 有能力做到这一点。无论如何,您可以创建我们自己的身份验证过滤器,并从HttpRequest 对象中获取此参数。 看看UsernamePasswordAuthenticationFilter。默认情况下在任何登录页面上使用它。 是的,我正在努力解决这个问题。但是我错过了那里的一些部分并遇到了错误。如果您有一些想法或工作示例(配置),请告诉我。 【参考方案1】:

详细阐述@Vacuum 的评论

这是一个简单的方法(未经测试,但我相信这会奏效)

    创建一个新类ExUsernamePasswordAuthenticationFilter,它将扩展默认过滤器并获取附加参数并将其存储在会话中。它看起来像这样:
public class ExUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter 
    
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException 
        final String dbValue = request.getParameter("dbParam");
        request.getSession().setAttribute("dbValue", dbValue);

        return super.attemptAuthentication(request, response); 
     

    在您的 UserDetailsService 实现中,修改您的实现:
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException;

获取步骤 1) 中的过滤器提供的会话变量。

    在您的 <http /> 安全设置中,使用您的自定义过滤器覆盖默认过滤器
<custom-filter ref="beanForYourCustomFilterFromStep1" position="FORM_LOGIN_FILTER"/>

有关自定义过滤器的更多信息,请参阅文档的这一部分:http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#ns-custom-filters

【讨论】:

非常感谢您的信息。我实现了,效果很好。 这样做的侵入性较小,而且不会违反UserDetailsService 的合同。我同意@sourcedelica。也可以将我的answer 转至类似问题以获取更详细的示例。 @Bhas,你是如何修改 UserDetailsS​​ervice 来获取会话变量的?【参考方案2】:

有很多方法可以做到这一点,但官方的方法是使用自定义 AuthenticationDetailsAuthenticationDetailsSource,分别继承 Spring 的 WebAuthenticationDetailsWebAuthenticationDetailsSource。将额外字段添加到自定义 WebAuthenticationDetails 并让自定义 WebAuthenticationDetailsSource 从请求中获取数据以填充该字段。

在 Spring Security 3.1 中,使用 &lt;form-login&gt; 元素的 authentication-details-source-ref 属性很容易配置。

在 3.0 中,您必须使用 BeanPostProcessor。 using a BeanPostProcessor to configure a custom WebAuthenticationDetailsSource 上的 Spring Security FAQ 中有一个示例。

完成此操作后,您可以调用 SecurityContextHolder.getContext().getAuthentication().getDetails() 来访问您的额外字段。

【讨论】:

感谢 Ericacm,他们已经实施了 Taylor Mingos 提供的解决方案,对我来说效果很好。 感谢自定义 AuthenticationDetails 的想法。这很有趣。你说它是官方的,但我找不到任何关于该技术的官方文档。有吗? 没有太多文档 - 你必须从 UsernamePasswordAuthenticationFilter 的工作原理中推断出来 - 它使用 WebAuthenticationDetailsSource 并将 WebAuthenticationDetails 添加到 UsernamePasswordAuthenticationToken【参考方案3】:

sourcedelica 使用AuthenticationDetailsSource 和自定义AuthenticationDetails 提及。 这是一个例子。

authentication-details-source-ref 属性与bean id customWebAuthenticationDetailsSource 添加到form-login

<security:http>
    <security:intercept-url pattern="/**" access="..." />
    <security:form-login authentication-details-source-ref="customWebAuthenticationDetailsSource" login-page="..." />
    <security:logout logout-success-url="..." />
</security:http>

创建一个新类CustomWebAuthenticationDetailsSource

package security;

import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.web.authentication.WebAuthenticationDetails;

import javax.servlet.http.HttpServletRequest;

public class CustomWebAuthenticationDetailsSource implements AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> 
    @Override
    public WebAuthenticationDetails buildDetails(HttpServletRequest context) 
        return new CustomWebAuthenticationDetails(context);
    

以及相关的CustomWebAuthenticationDetails:

package security;

import org.springframework.security.web.authentication.WebAuthenticationDetails;
import javax.servlet.http.HttpServletRequest;

public class CustomWebAuthenticationDetails extends WebAuthenticationDetails 

    private final String yourParameter;

    public CustomWebAuthenticationDetails(HttpServletRequest request) 
        super(request);
        yourParameter = request.getParameter("yourParameter");
    

    public String getyourParameter() 
        return yourParameter;
    

    //TODO override hashCode, equals and toString to include yourParameter
    @Override
    public int hashCode()  /* collapsed */ 
    @Override
    public boolean equals(Object obj)  /* collapsed */ 
    @Override
    public String toString()  /* collapsed */ 

【讨论】:

那么我该如何使用 CustomWebAuthenticationDetails 见@sourcedelica 回答:SecurityContextHolder.getContext().getAuthentication().getDetails() 对我不起作用。 AuthenticationDetailsS​​ource 在登录后调用 buildDetails 并且我的 GET 参数不再存在。使用 Spring 3.1.6 @raupach 您的安全配置一定有问题。如果您愿意,可以从Github 获取我的示例进行调试。 @diegosahagun 在调用JdbcDaoImpl.loadUsersByUsername 时,SecurityContext 没有填充当前的Authentication,因此null 引用。在您的情况下,您需要一个自定义的AuthenticationProvider,它能够处理您的额外参数。在这种情况下,您不能UserDetailsService。也许您可以在单独的问题中详细说明您的问题并获得多个答案。【参考方案4】:

@user1322340 没有提供在 loadUserByUsername 函数中获取会话属性的实现细节:

第 1 步:按照@user1322340 提供的所有步骤进行操作

第 2 步: 您需要像这样在 web.xml 中添加一项配置:

<listener>
    <listener-class>
       org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>

第 3 步: 使用此类代码获取属性:

RequestContextHolder.getRequestAttributes().getAttribute("yourAttributeName", RequestAttributes.SCOPE_SESSION);

第 4 步:在 spring 安全配置中注册您的过滤器。 如果您收到错误“authenticationManager must be specified”。在配置中注册过滤器后。您需要为扩展过滤器设置一个 authenticationManagerBean 并以这种方式进行配置:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter 
    @Bean
    public ExUsernamePasswordAuthenticationFilter exUsernamePasswordAuthenticationFilter()
            throws Exception 
        ExUsernamePasswordAuthenticationFilter exUsernamePasswordAuthenticationFilter = new ExUsernamePasswordAuthenticationFilter();
        exUsernamePasswordAuthenticationFilter
                .setAuthenticationManager(authenticationManagerBean());
        return exUsernamePasswordAuthenticationFilter;
    

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
        return super.authenticationManagerBean();
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        RequestMatcher requestMatcher = new RequestMatcher() 
            @Override
            public boolean matches(HttpServletRequest httpServletRequest) 
                if (httpServletRequest.getRequestURI().indexOf("/api", 0) >= 0) 
                    return true;
                
                return false;
            
        ;

        http
                .addFilterBefore(exUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                ...
    

【讨论】:

【参考方案5】:

对于使用java配置的spring security 3.0或以上版本,以下简单步骤即可。

在 配置中 HttpSecurity 对象中的 UserNameandPasswordAuthenticationFilter。

http.addFilterBefore(new YourFilter(), UsernamePasswordAuthenticationFilter.class);

让过滤器有这样一行来获取您的所需字段 请求会话。

if(requestPath != null &&requestPath.equals("/login") ) 
        session.setAttribute("yourParam",req.getParameter("yourParam"));
    

稍后您可以从任何类的会话中获取参数值:

String yourParam =(String)request.getSession().getAttribute("yourParam");

【讨论】:

【参考方案6】:

如果您使用自定义AuthenticationProvider,则有一种更简单的方法。您可以注入 HttpServletRequest 并检索您的额外参数:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider 

    @Autowired(required = false)
    private HttpServletRequest request;

    @Autowired
    private MyAccountService myAccountService;

    @Override
    public Authentication authenticate(Authentication authentication) 

        System.out.println("request testing= " + request.getParameter("testing"));

        .....
    

    @Override
    public boolean supports(Class<?> authentication) 
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    

【讨论】:

【参考方案7】:

简单的方法:

1) 注册RequestContextListener

@Bean
public RequestContextListener requestContextListener()
    return new RequestContextListener();

2) 到主类:

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.
currentRequestAttributes()).
getRequest();

3) 之后我们可以在自定义标题中获取参数:

request.getHeader("OrganizationId")

【讨论】:

【参考方案8】:

最简单的方法,只需两步:

第 1 步。

在 web.xml 中添加如下监听器:

<listener>
    <listener-class>
        org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value></param-value>
</context-param>

第 2 步。

在你想要获取额外参数的类方法中添加以下内容:

RequestAttributes attribs = RequestContextHolder.getRequestAttributes();

if (RequestContextHolder.getRequestAttributes() != null) 
    HttpServletRequest request = ((ServletRequestAttributes) attribs).getRequest();

现在您可以通过以下方式获取您的附加参数,假设附加参数名为“loginType”:

request.getParameter("loginType") 

【讨论】:

以上是关于如何使用 Spring Security 登录页面传递附加参数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用自定义登录页面纠正 Spring Security 异常?

如何使初始登陆页面不是spring security的登录表单?

如何使用 Spring Security 实现登录页面以使其与 Spring Web 流一起使用?

如何使用 Spring Security 在 login.jsp 页面显示登录错误消息?

如何在 Spring Security 中通过 jdbc 身份验证使用自定义登录页面

使用 Spring Security 成功登录后如何将用户转发回所需的受保护页面