在我的(java spring mvc + mysql application,thymeleaf)中实现spring security之后,身份验证发生了一些奇怪的事情

Posted

技术标签:

【中文标题】在我的(java spring mvc + mysql application,thymeleaf)中实现spring security之后,身份验证发生了一些奇怪的事情【英文标题】:After implementing spring security in my (java spring mvc + mysql application, thymeleaf) something weird happens with the authentication 【发布时间】:2018-07-03 04:17:24 【问题描述】:

具有自定义用户详细信息服务。如果我输入的 URL 未列在安全配置中的全部许可列表中。它将被重定向到登录并作为错误登录处理。然后直接如果我用正确的用户名和密码登录,它不会被重定向到默认的成功url,它会转到我之前输入的错误url。这是什么意思?我的代码有什么问题。请帮我!

这是我的用户详细信息服务实现

   @Service
   public class UserDetailsServiceImpl implements UserDetailsService
   @Autowired
   private UserRepository userRepository;

  @Override
  @Transactional(readOnly = true)
  public UserDetails loadUserByUsername(String email) throws 
  UsernameNotFoundException 

   User user = userRepository.findByEmail(email);
           Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
          for (Role role : user.getRoles())
          grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
    
    return new org.springframework.security.core.userdetails.User(user.getEmail(), 
 user.getPassword(), grantedAuthorities);

这是我的安全配置类

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter 

@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

 @Autowired
 private UserDetailsService userDetailsService;

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

 @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() 
        return new BCryptPasswordEncoder();
    


@Override
protected void configure(HttpSecurity http) throws Exception 

    http.
        authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers("/signin").permitAll()

            .antMatchers("/confirm").permitAll()
            .antMatchers("/index").permitAll()
            .antMatchers("/adminpage").permitAll()
            .antMatchers("/register").permitAll()
            .antMatchers("/login").permitAll()

   .antMatchers("/library/**","/admin").hasAuthority("ADMIN").anyRequest()
            .authenticated().and().csrf().disable().formLogin()
            .loginPage("/login").failureUrl("/login?error=true")
            .defaultSuccessUrl("/index")
            .usernameParameter("email")
            .passwordParameter("password")
            .and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/").and().exceptionHandling()
            .accessDeniedPage("/access-denied");



@Override
public void configure(WebSecurity web) throws Exception 
    web
       .ignoring()
       .antMatchers("/resources/**", "/static/**", "/css/**", "/js/**", 
"/img/**","/fonts/**")
 ;  

【问题讨论】:

更好地描述我的情况;如果我输入例如: localhost:8080/xyzkqwi 它将被重定向到 localhost:8080/login 我有一个控制器来处理这个页面,然后如果我使用正确的凭据登录到 mysql 数据库,它将被重定向到 localhost :8080/xyzkqwi 我有一个 HTTP 状态 404 错误 - 未找到而不是转到默认的成功 url 【参考方案1】:

这种行为是由于SavedRequestAwareAuthenticationSuccessHandler。如SavedRequestAwareAuthenticationSuccessHandler类的javadoc api所述:

一种身份验证成功策略,可以利用可能已由 ExceptionTranslationFilter 存储在会话中的 DefaultSavedRequest。当这样的请求被拦截并需要认证时,请求数据被存储以在认证过程开始之前记录原始目的地,并允许在重定向到相同URL时重新构造请求。此类负责在适当的情况下执行到原始 URL 的重定向。

身份验证成功后,它会根据以下情况决定重定向目的地:

如果 alwaysUseDefaultTargetUrl 属性设置为 true,则 defaultTargetUrl 将用于目标。会话中存储的任何 DefaultSavedRequest 都将被删除。 如果已在请求中设置了 targetUrlParameter,则该值将用作目标。任何 DefaultSavedRequest 都将再次被删除。 如果在 RequestCache 中找到 SavedRequest(由 ExceptionTranslationFilter 设置以在身份验证过程开始之前记录原始目标),将执行重定向到该原始目标的 Url。 SavedRequest 对象将保持缓存并在收到重定向请求时被拾取(请参阅 SavedRequestAwareWrapper)。 如果没有找到 SavedRequest,它将委托给基类。

如果您想跳过此行为并始终重定向到默认成功 url,您可以使用方法 defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) 而不是仅使用 defaultSuccessUrl(String defaultSuccessUrl) 来实现,当然,将第二个参数设置为布尔值,就这样:

@Override
protected void configure(HttpSecurity http) throws Exception 

    http.
        authorizeRequests()
            .antMatchers("/").permitAll()
            .antMatchers("/signin").permitAll()

            .antMatchers("/confirm").permitAll()
            .antMatchers("/index").permitAll()
            .antMatchers("/adminpage").permitAll()
            .antMatchers("/register").permitAll()
            .antMatchers("/login").permitAll()

   .antMatchers("/library/**","/admin").hasAuthority("ADMIN").anyRequest()
            .authenticated().and().csrf().disable().formLogin()
            .loginPage("/login").failureUrl("/login?error=true")
            .defaultSuccessUrl("/index", true)              
            .usernameParameter("email")
            .passwordParameter("password")
            .and().logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/").and().exceptionHandling()
            .accessDeniedPage("/access-denied");


这是 github repo 中 Spring security 的 AbstractAuthenticationFilterConfigurer 类中方法 defaultSuccessUrl(String defaultSuccessUrl)defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) 的源代码:

/**
     * Specifies where users will go after authenticating successfully if they have not
     * visited a secured page prior to authenticating. This is a shortcut for calling
     * @link #defaultSuccessUrl(String).
     *
     * @param defaultSuccessUrl the default success url
     * @return the @link FormLoginConfigurer for additional customization
     */
    public final T defaultSuccessUrl(String defaultSuccessUrl) 
        return defaultSuccessUrl(defaultSuccessUrl, false);
    

    /**
     * Specifies where users will go after authenticating successfully if they have not
     * visited a secured page prior to authenticating or @code alwaysUse is true. This
     * is a shortcut for calling @link #successHandler(AuthenticationSuccessHandler).
     *
     * @param defaultSuccessUrl the default success url
     * @param alwaysUse true if the @code defaultSuccesUrl should be used after
     * authentication despite if a protected page had been previously visited
     * @return the @link FormLoginConfigurer for additional customization
     */
    public final T defaultSuccessUrl(String defaultSuccessUrl, boolean alwaysUse) 
        SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
        handler.setDefaultTargetUrl(defaultSuccessUrl);
        handler.setAlwaysUseDefaultTargetUrl(alwaysUse);
        return successHandler(handler);

【讨论】:

以上是关于在我的(java spring mvc + mysql application,thymeleaf)中实现spring security之后,身份验证发生了一些奇怪的事情的主要内容,如果未能解决你的问题,请参考以下文章

java - 在spring mvc中按名称获取cookie值

如何在带有注解配置的spring mvc中使用spring数据

如何在 Spring MVC 中渲染局部视图

在我的 Spring MVC Magnolia 模块中获取 JCR Session 对象的更好方法是 LifeTimeJCRSessionUtil

spring mvc控制器映射不同的方式

在 Spring MVC 中使用 JavaScript 上传图像以进行预览