在 Spring Security UsernamePasswordAuthenticationFilter JWT 身份验证中设置自定义登录 url

Posted

技术标签:

【中文标题】在 Spring Security UsernamePasswordAuthenticationFilter JWT 身份验证中设置自定义登录 url【英文标题】:Set custom login url in Spring Security UsernamePasswordAuthenticationFilter JWT authentication 【发布时间】:2018-04-04 00:01:33 【问题描述】:

我正在关注 this auth0's tutorial 以使用 JWT 保护我的应用程序。

我最终得到了以下 WebSecurity 配置:

@EnableWebSecurity
@AllArgsConstructor(onConstructor = @__(@Autowired))
public class WebSecurity extends WebSecurityConfigurerAdapter 

    private final UserDetailsService userDetailsService;
    private final BCryptPasswordEncoder passwordEncoder;

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.formLogin()
                .and().cors()
                .and().csrf()
                .disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, REGISTER_URL).permitAll()
                .antMatchers(HttpMethod.POST, LOGIN_URL).permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilter(new JWTAuthorizationFilter(authenticationManager()))
                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                // This disables session creation on Spring Security
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
    

    @Bean
    public CorsConfigurationSource corsConfigurationSource() 
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    


以及以下 JWTAuthenticationFilter:

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter 

    private final AuthenticationManager authenticationManager;

    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) 
        this.authenticationManager = authenticationManager;
    

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException 
        try 
            ApplicationUser credentials = new ObjectMapper().readValue(request.getInputStream(), ApplicationUser.class);
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            credentials.getUsername(),
                            credentials.getPassword(),
                            new ArrayList<>()
                    )
            );
         catch (IOException e) 
            throw new RuntimeException(e);
        
    

    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException 
        String token = Jwts.builder()
                .setSubject(((User) authResult.getPrincipal()).getUsername())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
                .compact();
        response.addHeader(HEADER_STRING, TOKEN_PREFIX + token);
    

目前,该应用接受/login URL 上的POST 请求。我想知道如何将 URL 更改为,比如说,/api/auth/login。有没有办法将 URL 字符串注入身份验证过滤器或在安全配置中以某种方式设置它?

【问题讨论】:

【参考方案1】:

您正在扩展 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter,它本身扩展 org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter。在最后一个类中,有一个名为 setFilterProcessesUrl 的 setter 旨在执行此操作:

setFilterProcessesUrl

public void setFilterProcessesUrl(String filterProcessesUrl)

设置确定是否需要身份验证的 URL

参数:filterProcessesUrl

This 是指向该 javadoc 部分的链接

所以在你的WebSecurityConfigurerAdapter 中你可以这样做:

@Bean
public JWTAuthenticationFilter getJWTAuthenticationFilter() 
    final JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManager());
    filter.setFilterProcessesUrl("/api/auth/login");
    return filter;

然后在同一类中的 configure 方法中只引用它而不是创建新实例:

.addFilter(getJWTAuthenticationFilter())

【讨论】:

谢谢,这很有魅力!不过,您的回答中有一个小疏忽,您将 JWTAuthorizationFilter 与 JWTAuthenticationFilter 混淆了。但是,它仍然可以完美运行。 我们可以在springboot中用oauth2自己的资源服务器做同样的事情吗? @YasirShabbirChoudhary 我想是这样,但完全不确定。你可以试一试,如果它不起作用,你可以发布一个问题,我会检查它 @jilumietu - 谢谢你的回答。我有一个问题要问你。是否可以通过这种方式使 Spring 安全性允许其他方法如 DELETE 或 PUT 到 filterprocessesUrl? @KavithaKarunakaran,我没有测试过它,但它应该。方法AbstractAuthenticationProcessingFilter.setFilterProcessesUrl(String filterProcessesUrl) 内部调用setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(filterProcessesUrl));。构造函数AntPathRequestMatcher(String filterProcessesUrl)HttpMethod httpMethod 属性初始化为null,这使得public boolean matches(HttpServletRequest request) 方法不会评估传入的HttpServletRequest 方法。所以根据这个,其他方法肯定可以工作【参考方案2】:

一个小的改进可能是只使用自定义进程 url 创建您的过滤器并使用它而不创建我认为您在此处以外的任何地方都不需要的 bean。

JWTAuthenticationFilter authenticationFilter = new JWTAuthenticationFilter(authenticationManager());
authenticationFilter.setFilterProcessesUrl("/mobile/login");
....
.and()
.addFilter(authenticationFilter)
.addFilter(new JWTAuthenticationFilter(authenticationManager()))

【讨论】:

以上是关于在 Spring Security UsernamePasswordAuthenticationFilter JWT 身份验证中设置自定义登录 url的主要内容,如果未能解决你的问题,请参考以下文章

在运行时延迟初始化 Spring Security + 重新加载 Spring Security 配置

在使用 Oauth、SAML 和 spring-security 的多租户的情况下从 spring-security.xml 中获取错误

spring-security

Spring Security 入门(1-11)Spring Security - 匿名认证

使用 spring-session 和 spring-cloud-security 时,OAuth2ClientContext (spring-security-oauth2) 不会保留在 Redis

spring boot 整合spring security中spring security版本升级的遇到的坑