用户登录时的 Spring Boot 安全自定义消息

Posted

技术标签:

【中文标题】用户登录时的 Spring Boot 安全自定义消息【英文标题】:Spring boot security custom messages while user login 【发布时间】:2018-02-25 15:04:18 【问题描述】:

我正在尝试在我的 Spring Boot 应用程序中集成 Spring Security。一切正常,但是如果帐户已过期或帐户被锁定,我该如何显示消息?另外,我不想显示基于参数的错误消息,例如http://localhost:8080/login?error

这是我当前的代码:login.html

        <div th:if="$param.error" class="alert alert-danger">
            Invalid username or password.
        </div>


        <h3>Sign in to continue</h3>

        <form th:action="@/login" name="loginForm"  method="POST">
            <div class="form-group">
                <label for="userNameInput">Username</label>
                <input type="text" class="form-control" id="userNameInput" name="username" placeholder="Username" />
            </div>
            <div class="form-group">
                <label for="passwordInput">Password</label>
                <input type="password" class="form-control" id="passwordInput"  name="password" placeholder="Password"  />
            </div>

            <button type="submit" class="btn btn-success">Login</button>
        </form>

WebSecurityConfig.java

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

  @Autowired
  private CustomUserDetailsService customUserDetailsService;

  @Override
  protected void configure(HttpSecurity http) throws Exception 

    http
            .authorizeRequests()
            .antMatchers("/", "/css/**", "/js/**","/login/**").permitAll()
            .anyRequest().authenticated()
            .and().formLogin().loginPage("/login").defaultSuccessUrl("/dashboard")
            .and().logout().logoutSuccessUrl("/");
  

  @Autowired
  public void configAuthBuilder(AuthenticationManagerBuilder auth) throws Exception 
    auth.userDetailsService(customUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
  


CustomUserDetailsS​​ervice.java

@Service
public class CustomUserDetailsService implements UserDetailsService 

@Autowired
UserRepository userRepository;

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

    //query for user from DB
    User user = userRepository.findByUsername(username);


    if (user == null) 
        throw new UsernameNotFoundException(username);
    

    Date today = new Date();

    //do check if account expired / suspended / deleted
    Boolean isAcconutExpired = false;
    Boolean status = false;

    if (user.getExpireOn() != null && today.before(user.getExpireOn())) 
        isAcconutExpired = false;
    


    if(user.getStatus() == 1)
        status = true;
    

    return new org.springframework.security.core.userdetails.User(user.getUsername(),
            user.getPassword(),
            status,
            !isAcconutExpired,
            true,
            true,
            getAuthorities(user));



private List<GrantedAuthority> getAuthorities(User user) 
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    authorities.add(new SimpleGrantedAuthority("USER"));
    return authorities;
   

UserRepository.java

@Repository
public interface UserRepository extends CrudRepository<User, Long> 
  User findByUsername(String username);

【问题讨论】:

如果您使用 jsp 页面,您可以将页面重定向到 Login.jsp?status=error 并在您的 jsp 页面代码中获取查询字符串(如果它不为空)引发自定义消息 @MohammadMirzaeyan - 我没有使用 jsp。 【参考方案1】:

这条消息有点旧,但我目前面临同样的问题。

所以,首先,你必须创建一个自定义的 Authentication Provider 实例,让 HideUserNotFoundExceptions 被传递给控制器​​:

public AuthenticationProvider daoAuthenticationProvider() 
    DaoAuthenticationProvider impl = new DaoAuthenticationProvider();
    impl.setUserDetailsService(customUserDetailsService);
    impl.setPasswordEncoder(new BCryptPasswordEncoder());
    impl.setHideUserNotFoundExceptions(false) ;
    return impl ;

此外,您应该在 AuthenticationProviderBuilder 中添加此提供程序,而不是添加 customDetailService(添加 customDetailService 将添加另一个提供程序):

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
    auth.authenticationProvider(daoAuthenticationProvider());


有了这个,您现在可以捕获 UserNotFoundException 而不是基本的 BadCredentialsException

因此,仍然需要为这两个异常显示自定义错误消息。 Badcredentials 异常由 SpringSecurity 直接抛出,并带有基于 I18n 消息 AbstractUserDetailsAuthenticationProvider.badCredentials 的错误消息(参见 spring security here 中的实现):

throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));

到期和帐户锁定也是如此。 所以我建议你改变你的 CustomUserDetailsS​​ervice.java 类来做同样的类似的代码错误:

if (user == null) 
   throw new UsernameNotFoundException(SpringSecurityMessageSource.getAccessor().getMessage("AbstractUserDetailsAuthenticationProvider.UserUnknown",new Object[] login,"User is not known"));

在此之后,您可以在 message.properties 中添加这些行:

AbstractUserDetailsAuthenticationProvider.UserUnknown = 0 was not found.
AbstractUserDetailsAuthenticationProvider.badCredentials = Password is bad
AbstractUserDetailsAuthenticationProvider.credentialsExpired = User credentials have expired
AbstractUserDetailsAuthenticationProvider.disabled = User is disabled
AbstractUserDetailsAuthenticationProvider.expired = User account has expired
AbstractUserDetailsAuthenticationProvider.locked = User account is locked

并在 login.html 中显示错误消息:

            <div class="dialog-row">
                <label th:if="$param.error" th:text="$session['SPRING_SECURITY_LAST_EXCEPTION'].message" class="text-center redText">Mot de passe inconnu</label>                   
            </div>

【讨论】:

我也需要这段代码,但我不工作。我不明白$session['SPRING_SECURITY_LAST_EXCEPTION'].message 来自哪里!!以及消息如何从message.properties 显示?我需要在顶部添加任何会话变量吗? 你好@varman。 $session['SPRING_SECURITY_LAST_EXCEPTION'].message 由 Spring Security 在 session 中直接设置($session[XXX] 是 Thymeleaf 获取此异常的方式) Spring security 已经在 message.properties 中搜索此值:AbstractUserDetailsAuthenticationProvider。 badCredentials AbstractUserDetailsAuthenticationProvider.credentialsExpired AbstractUserDetailsAuthenticationProvider.disabled[...] “AbstractUserDetailsAuthenticationProvider.UserUnknown”是由 CustomUserDetailsS​​ervice 创建的。你的错误是什么?

以上是关于用户登录时的 Spring Boot 安全自定义消息的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot 用户自定义访问控制,自定义登录页面,退出,用户信息获取

Spring boot 用户自定义访问控制,自定义登录页面,退出,用户信息获取

spring boot 使用拦截器 实现 用户登录拦截

如何将自定义凭据传递给 Spring Boot ldap

无法在 Spring Boot Security 中登录到我的自定义登录页面

spring boot整合 spring security之自定义认证