登录 Spring 安全重定向到错误 Thymeleaf 和基于 Java 的配置

Posted

技术标签:

【中文标题】登录 Spring 安全重定向到错误 Thymeleaf 和基于 Java 的配置【英文标题】:Login in Spring security redirects to error Thymeleaf and Java based Configuration 【发布时间】:2019-07-17 14:34:06 【问题描述】:

我正在尝试在我的 Spring 应用程序上实现基于角色的身份验证。但是,即使凭据正确,我也会被重定向到 http://localhost:8080/login?error=true,并且 hibernate 会毫无问题地获取登录凭据。

这是我的 SecurityConfig 类

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter 

@Autowired
private UserDetailsService userDetailsService;

@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

private static final String[] PUBLIC_MATCHERS = 
        "/css/**","/js/**","/image/**","/fonts/**","/webfonts/**","/register","/login","/actuator/**"
;


@Override
protected void configure(HttpSecurity http) throws Exception 
    http.authorizeRequests()        
        .antMatchers(PUBLIC_MATCHERS).permitAll().anyRequest().authenticated()
        .antMatchers("/addShipping").hasAuthority("ROLE_USER")
        .antMatchers("/item/add").hasAuthority("ROLE_ADMIN")
        .and().formLogin().loginPage("/login").failureUrl("/login?error=true").defaultSuccessUrl("/",true)
        .and()
            .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
            .logoutSuccessUrl("/")
            .deleteCookies("remember-me")
            .permitAll()
        .and().rememberMe()
        .and().exceptionHandling().accessDeniedPage("/denied");


  

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

这是我的登录控制器

@RequestMapping(value = "/login", method = RequestMethod.GET) 
public String login(Model model)  

    return "login"; 

我的角色类

@Data
@Entity
public class Role 

@Id
@Column(name="role_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotNull
@Column(name ="role")
private String role;

用户类

@Entity
@Data
public class User implements UserDetails 

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name ="user_id")
private Long id;

@OneToOne(cascade = CascadeType.ALL)
private Cart cart;

@OneToMany(mappedBy = "user")
private List<Order> order;

@NotEmpty(message="username is required")
private  String username;

@NotEmpty(message="password is required")
private  String password;

@NotEmpty(message="password is required")
private  String fullname;

private int enabled;

@NotBlank(message="Pleace Provide your Role")
private String role;


@NotEmpty
@Email 
private String email;

@NotEmpty(message="phone is required")
private  String phone;

@ManyToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
@JoinTable( name="user_role", 
            joinColumns= @JoinColumn(name="user_id"),
            inverseJoinColumns= @JoinColumn(name="role_id"))
private Set<Role> roles;

@Override
public Collection<? extends GrantedAuthority> getAuthorities() 

    Set<GrantedAuthority> authorities = roles
            .stream()
            .map(role -> new SimpleGrantedAuthority(role.getRole()))
            .collect(Collectors.toSet());


    return authorities;


@Override
public boolean isAccountNonExpired() 
    return true;


@Override
public boolean isAccountNonLocked() 
    return true;


@Override
public boolean isCredentialsNonExpired() 
    return true;


@Override
public boolean isEnabled() 
    return true;
    



登录模板

                                <div th:if="$param.error != null" style="color: red;">Incorrect credentials</div>
                                <form th:action="@/login" method="post">
                                    <div class="form-group"><input required="required" type="text" class="form-control" id="username" name="username" placeholder="Username" /></div>
                                    <div class="form-group"> <input required="required" type="password" class="form-control" id="password" name="password" placeholder="Password" /></div>
                                    <div class="col-sm-6 col-sm-offset-3 text-center">
                                        <input type="checkbox" class="" name="remember" id="remember">
                                        <label for="remember"> Remember Me</label>
                                    </div>

                                    <div class="col-sm-6 col-sm-offset-3"> <button  type="submit" class="btn btn-primary btn-block">Log in</button> </div>
                                </form>

[编辑]

用户服务

public interface UserService extends UserDetailsService 

    void save(User user);

    User findUserByUsername(String username);


用户服务实现

@Service
public class UserServicelmpl implements UserService 

  private BCryptPasswordEncoder bCryptPasswordEncoder;

  private UserRepository userRepository;

  private RoleRepository roleRepository;



  @Autowired
  public UserServicelmpl(UserRepository userRepository, RoleRepository 
roleRepository, BCryptPasswordEncoder bCryptPasswordEncoder) 
    this.userRepository = userRepository;
    this.roleRepository = roleRepository;
    this.bCryptPasswordEncoder = bCryptPasswordEncoder;
  

  @Override
  public UserDetails loadUserByUsername(String username)
      throws UsernameNotFoundException 
    User user = userRepository.findByUsername(username);

    if (user != null) 
      return user;
    

    throw new UsernameNotFoundException("The user with user name of '" + 
username + "' is not found");
   

  @Override
  public void save(User user) 

      user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
      user.setEnabled(true);
      String Role = "ROLE_"+ user.getRole();
      Role userRole = roleRepository.findByRole(Role);
      user.setRoles(new HashSet<Role>(Arrays.asList(userRole)));
      userRepository.save(user);

  

  @Override
    public User findUserByUsername(String username) 
      return userRepository.findByUsername(username);
     

[编辑 2] 记录

o.s.s.a.dao.DaoAuthenticationProvider : 身份验证失败:密码与存储的值不匹配

w.a.UsernamePasswordAuthenticationFilter:身份验证请求失败:org.springframework.security.authentication.BadCredentialsException:凭据错误

org.springframework.security.authentication.BadCredentialsException:错误的凭据 在 org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:93) ~[spring-security-core-5.1.2.RELEASE.jar:5.1.2.RELEASE] 在 org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:166) ~[spring-security-core-5.1.2.RELEASE.jar:5.1.2.RELEASE]

【问题讨论】:

删除 .anyRequest().authenticated() 看看你是否登录成功。然后将其放在角色 antMatchers 之后。 已经试过了,没有运气! 那一定是一些愚蠢的错字/错误配置。我们需要更多信息。请将logging.level.org.springframework.security = DEBUG 添加到您的应用程序属性中,并将日志包含在您的问题中。 哦。我刚刚注意到您急切地加载角色,这意味着它是在密码检查发生之前加载的。因此,您也可能在数据库中存储了无效的凭据。您确定密码已正确编码存储在数据库中吗? 我已经更新了 logging.level.org.springframework.security = DEBUG 添加到我的 application.properties 文件 【参考方案1】:

尝试删除此方法:

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

Spring Boot 自动选择 UserDetailsServicePasswordEncoder bean。如果您覆盖此方法,它会使用完全不同的方式来创建 AuthenticationManager

【讨论】:

我删除了覆盖,没有什么不同...我在第二次编辑时添加了休眠日志 您的测试用户是否在数据库中设置了任何角色?我记不太清楚了,但我认为在 Spring Security 的某个地方有一个断言,即用户必须至少有一个身份验证。 我自己在数据库中的角色表中添加了 role_id 和角色

以上是关于登录 Spring 安全重定向到错误 Thymeleaf 和基于 Java 的配置的主要内容,如果未能解决你的问题,请参考以下文章

Spring 安全性和 Angular javascript 重定向到登录页面

会话超时后,Spring 安全性不会重定向到上次请求的页面登录

Spring安全重定向到登录并恢复之前输入的表单数据

使用Spring Social,Spring安全登录后重定向到原始URL?

Spring Oauth和安全性在登录成功后重定向到/ login

登录成功后访问登录页面时出现Spring安全访问被拒绝消息。它应该重定向到默认页面