用户登录时的 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());
CustomUserDetailsService.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"));
到期和帐户锁定也是如此。 所以我建议你改变你的 CustomUserDetailsService.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”是由 CustomUserDetailsService 创建的。你的错误是什么?以上是关于用户登录时的 Spring Boot 安全自定义消息的主要内容,如果未能解决你的问题,请参考以下文章
Spring boot 用户自定义访问控制,自定义登录页面,退出,用户信息获取
Spring boot 用户自定义访问控制,自定义登录页面,退出,用户信息获取