Springboot、Hibernate、Spring数据JPA-无法登录

Springboot、Hibernate、Spring数据JPA-无法登录,hibernate,security,authentication,jpa,spring-boot,Hibernate,Security,Authentication,Jpa,Spring Boot,我一直遵循此示例在我的网站上创建登录功能: 但是,我的登录尝试失败。从stacktrace中,发现一个用户名为“test”的用户,但是凭据出现了错误(我尝试了加密和未加密的密码,并且出现了相同的错误) 这是我的WebSecurityConfig.java文件: package com.sitename.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframew

我一直遵循此示例在我的网站上创建登录功能:

但是,我的登录尝试失败。从stacktrace中,发现一个用户名为“test”的用户,但是凭据出现了错误(我尝试了加密和未加密的密码,并且出现了相同的错误)

这是我的WebSecurityConfig.java文件:

package com.sitename.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import  org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.slf4j.*;

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {    
     auth.userDetailsService(userDetailsService);
 } 

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .authorizeRequests()
                .antMatchers("/users/logout").authenticated()
                .anyRequest().permitAll()
                .and()
            .formLogin()
                .loginPage("/users/login") 
                .usernameParameter("username").passwordParameter("password")
                .defaultSuccessUrl("/")
                .successHandler(successHandler())
                //.failureHandler(failureHandler())
                .permitAll();

    }

    @Bean
    public AuthenticationSuccessHandler successHandler() {
        log.info("Login success!");
        SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
        handler.setUseReferer(true);
        return handler;
    }

    //    @Bean
    //    public AuthenticationFailureHandler failureHandler() {
    //      log.info("Login failure - bad credentials");
    //      return failureHandler();
    //    }

   @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        log.info("Running configureGlobal method in WebSecurityConfig");
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
//        auth
//            .inMemoryAuthentication()
//                .withUser("user").password("password").roles("USER");
    }

    @Bean(name="passwordEncoder")
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
package com.sitename.domain.models;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;

@Entity
@Table(name = "users")
public class User implements Serializable {


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

@NotBlank
@NotEmpty
@NotNull
@Column(name = "username")
private String username;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "email")
private String email;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "password")
private String password;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "enabled")
private int enabled;

public User(String username, String password, String email, int enabled) {
    this.username = username;
    this.password = password;
    this.email = email;
    this.enabled = enabled;
}

public User(User user) {
    this.id = user.id;
    this.username = user.username;
    this.email = user.email;
    this.password = user.password;
    this.enabled = user.enabled;
}

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

public int getEnabled() {
    return enabled;
}

public void setEnabled(int enabled) {
    this.enabled = enabled;
}


public User() {}

}
package com.sitename.domain.models;

import javax.persistence.*;

@Entity
@Table(name = "user_roles")
public class UserRole {

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

@Column(name = "user_id")
private Long userId;

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

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public Long getUserId() {
    return userId;
}

public void setUserId(Long userId) {
    this.userId = userId;
}

public String getRole() {
    return role;
}

public void setRole(String role) {
    this.role = role;
}

}
package com.sitename.domain.repositories;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.sitename.domain.models.User;

@Repository
public interface UserRepository extends CrudRepository<User, Long> {

public User findByUsername(String username);
}
package com.sitename.domain.repositories;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.sitename.domain.models.UserRole;

import java.util.List;

@Repository
public interface UserRoleRepository extends CrudRepository<UserRole, Long> {

@Query("select a.role from UserRole a, User b where b.username=?1 and a.id=b.id")
public List<String> findRoleByUsername(String username);
 }
package com.sitename.security;

import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import com.sitename.domain.models.User;

public class CustomUserDetails extends User implements UserDetails {

private static final long serialVersionUID = 1L;
private List<String> userRoles;

public CustomUserDetails(User user, List<String> userRoles) {
    super(user);
    this.userRoles = userRoles;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    String roles=StringUtils.collectionToCommaDelimitedString(userRoles);           
    return AuthorityUtils.commaSeparatedStringToAuthorityList(roles);
}

@Override
public String getPassword() {
    return super.getPassword();
}

@Override
public String getUsername() {
    return super.getUsername();
}

@Override
public boolean isAccountNonExpired() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isAccountNonLocked() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isCredentialsNonExpired() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isEnabled() {
    // TODO Auto-generated method stub
    return false;
}

}
package com.sitename.security;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import org.slf4j.*;

import com.sitename.domain.models.User;
import com.sitename.domain.repositories.UserRepository;
import com.sitename.domain.repositories.UserRoleRepository;

@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class);

private final UserRepository userRepository;
private final UserRoleRepository userRoleRepository;

@Autowired
public CustomUserDetailsService(UserRepository userRepository, UserRoleRepository userRoleRepository) {
    this.userRepository = userRepository;
    this.userRoleRepository = userRoleRepository;
}


@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findByUsername(username);
    log.info("Username entered: " +  username);
    if(user.equals(null)) {
        log.info("User with username: " + username + " not found.");
        throw new UsernameNotFoundException("No user found with username: " + username);
    } else {
        log.info("User found with username " + user.getUsername() + " and password " + user.getPassword());
        List<String> userRoles = userRoleRepository.findRoleByUsername(username);
        return new CustomUserDetails(user, userRoles);
    }
}


}
package com.sitename;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.orm.jpa.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootApplication
@EnableJpaRepositories(basePackages = "com.sitename.domain.repositories")
@EntityScan(basePackages = "com.sitename.domain.models")
public class WebsiteApplication {

private static final Logger log = LoggerFactory.getLogger(WebsiteApplication.class);

public static void main(String[] args) {
    SpringApplication.run(WebsiteApplication.class, args);
}
}
这是我的用户实体:

package com.sitename.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import  org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.slf4j.*;

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {    
     auth.userDetailsService(userDetailsService);
 } 

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .authorizeRequests()
                .antMatchers("/users/logout").authenticated()
                .anyRequest().permitAll()
                .and()
            .formLogin()
                .loginPage("/users/login") 
                .usernameParameter("username").passwordParameter("password")
                .defaultSuccessUrl("/")
                .successHandler(successHandler())
                //.failureHandler(failureHandler())
                .permitAll();

    }

    @Bean
    public AuthenticationSuccessHandler successHandler() {
        log.info("Login success!");
        SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
        handler.setUseReferer(true);
        return handler;
    }

    //    @Bean
    //    public AuthenticationFailureHandler failureHandler() {
    //      log.info("Login failure - bad credentials");
    //      return failureHandler();
    //    }

   @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        log.info("Running configureGlobal method in WebSecurityConfig");
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
//        auth
//            .inMemoryAuthentication()
//                .withUser("user").password("password").roles("USER");
    }

    @Bean(name="passwordEncoder")
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
package com.sitename.domain.models;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;

@Entity
@Table(name = "users")
public class User implements Serializable {


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

@NotBlank
@NotEmpty
@NotNull
@Column(name = "username")
private String username;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "email")
private String email;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "password")
private String password;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "enabled")
private int enabled;

public User(String username, String password, String email, int enabled) {
    this.username = username;
    this.password = password;
    this.email = email;
    this.enabled = enabled;
}

public User(User user) {
    this.id = user.id;
    this.username = user.username;
    this.email = user.email;
    this.password = user.password;
    this.enabled = user.enabled;
}

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

public int getEnabled() {
    return enabled;
}

public void setEnabled(int enabled) {
    this.enabled = enabled;
}


public User() {}

}
package com.sitename.domain.models;

import javax.persistence.*;

@Entity
@Table(name = "user_roles")
public class UserRole {

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

@Column(name = "user_id")
private Long userId;

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

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public Long getUserId() {
    return userId;
}

public void setUserId(Long userId) {
    this.userId = userId;
}

public String getRole() {
    return role;
}

public void setRole(String role) {
    this.role = role;
}

}
package com.sitename.domain.repositories;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.sitename.domain.models.User;

@Repository
public interface UserRepository extends CrudRepository<User, Long> {

public User findByUsername(String username);
}
package com.sitename.domain.repositories;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.sitename.domain.models.UserRole;

import java.util.List;

@Repository
public interface UserRoleRepository extends CrudRepository<UserRole, Long> {

@Query("select a.role from UserRole a, User b where b.username=?1 and a.id=b.id")
public List<String> findRoleByUsername(String username);
 }
package com.sitename.security;

import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import com.sitename.domain.models.User;

public class CustomUserDetails extends User implements UserDetails {

private static final long serialVersionUID = 1L;
private List<String> userRoles;

public CustomUserDetails(User user, List<String> userRoles) {
    super(user);
    this.userRoles = userRoles;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    String roles=StringUtils.collectionToCommaDelimitedString(userRoles);           
    return AuthorityUtils.commaSeparatedStringToAuthorityList(roles);
}

@Override
public String getPassword() {
    return super.getPassword();
}

@Override
public String getUsername() {
    return super.getUsername();
}

@Override
public boolean isAccountNonExpired() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isAccountNonLocked() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isCredentialsNonExpired() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isEnabled() {
    // TODO Auto-generated method stub
    return false;
}

}
package com.sitename.security;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import org.slf4j.*;

import com.sitename.domain.models.User;
import com.sitename.domain.repositories.UserRepository;
import com.sitename.domain.repositories.UserRoleRepository;

@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class);

private final UserRepository userRepository;
private final UserRoleRepository userRoleRepository;

@Autowired
public CustomUserDetailsService(UserRepository userRepository, UserRoleRepository userRoleRepository) {
    this.userRepository = userRepository;
    this.userRoleRepository = userRoleRepository;
}


@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findByUsername(username);
    log.info("Username entered: " +  username);
    if(user.equals(null)) {
        log.info("User with username: " + username + " not found.");
        throw new UsernameNotFoundException("No user found with username: " + username);
    } else {
        log.info("User found with username " + user.getUsername() + " and password " + user.getPassword());
        List<String> userRoles = userRoleRepository.findRoleByUsername(username);
        return new CustomUserDetails(user, userRoles);
    }
}


}
package com.sitename;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.orm.jpa.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootApplication
@EnableJpaRepositories(basePackages = "com.sitename.domain.repositories")
@EntityScan(basePackages = "com.sitename.domain.models")
public class WebsiteApplication {

private static final Logger log = LoggerFactory.getLogger(WebsiteApplication.class);

public static void main(String[] args) {
    SpringApplication.run(WebsiteApplication.class, args);
}
}
我的用户角色实体:

package com.sitename.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import  org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.slf4j.*;

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {    
     auth.userDetailsService(userDetailsService);
 } 

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .authorizeRequests()
                .antMatchers("/users/logout").authenticated()
                .anyRequest().permitAll()
                .and()
            .formLogin()
                .loginPage("/users/login") 
                .usernameParameter("username").passwordParameter("password")
                .defaultSuccessUrl("/")
                .successHandler(successHandler())
                //.failureHandler(failureHandler())
                .permitAll();

    }

    @Bean
    public AuthenticationSuccessHandler successHandler() {
        log.info("Login success!");
        SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
        handler.setUseReferer(true);
        return handler;
    }

    //    @Bean
    //    public AuthenticationFailureHandler failureHandler() {
    //      log.info("Login failure - bad credentials");
    //      return failureHandler();
    //    }

   @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        log.info("Running configureGlobal method in WebSecurityConfig");
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
//        auth
//            .inMemoryAuthentication()
//                .withUser("user").password("password").roles("USER");
    }

    @Bean(name="passwordEncoder")
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
package com.sitename.domain.models;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;

@Entity
@Table(name = "users")
public class User implements Serializable {


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

@NotBlank
@NotEmpty
@NotNull
@Column(name = "username")
private String username;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "email")
private String email;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "password")
private String password;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "enabled")
private int enabled;

public User(String username, String password, String email, int enabled) {
    this.username = username;
    this.password = password;
    this.email = email;
    this.enabled = enabled;
}

public User(User user) {
    this.id = user.id;
    this.username = user.username;
    this.email = user.email;
    this.password = user.password;
    this.enabled = user.enabled;
}

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

public int getEnabled() {
    return enabled;
}

public void setEnabled(int enabled) {
    this.enabled = enabled;
}


public User() {}

}
package com.sitename.domain.models;

import javax.persistence.*;

@Entity
@Table(name = "user_roles")
public class UserRole {

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

@Column(name = "user_id")
private Long userId;

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

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public Long getUserId() {
    return userId;
}

public void setUserId(Long userId) {
    this.userId = userId;
}

public String getRole() {
    return role;
}

public void setRole(String role) {
    this.role = role;
}

}
package com.sitename.domain.repositories;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.sitename.domain.models.User;

@Repository
public interface UserRepository extends CrudRepository<User, Long> {

public User findByUsername(String username);
}
package com.sitename.domain.repositories;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.sitename.domain.models.UserRole;

import java.util.List;

@Repository
public interface UserRoleRepository extends CrudRepository<UserRole, Long> {

@Query("select a.role from UserRole a, User b where b.username=?1 and a.id=b.id")
public List<String> findRoleByUsername(String username);
 }
package com.sitename.security;

import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import com.sitename.domain.models.User;

public class CustomUserDetails extends User implements UserDetails {

private static final long serialVersionUID = 1L;
private List<String> userRoles;

public CustomUserDetails(User user, List<String> userRoles) {
    super(user);
    this.userRoles = userRoles;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    String roles=StringUtils.collectionToCommaDelimitedString(userRoles);           
    return AuthorityUtils.commaSeparatedStringToAuthorityList(roles);
}

@Override
public String getPassword() {
    return super.getPassword();
}

@Override
public String getUsername() {
    return super.getUsername();
}

@Override
public boolean isAccountNonExpired() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isAccountNonLocked() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isCredentialsNonExpired() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isEnabled() {
    // TODO Auto-generated method stub
    return false;
}

}
package com.sitename.security;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import org.slf4j.*;

import com.sitename.domain.models.User;
import com.sitename.domain.repositories.UserRepository;
import com.sitename.domain.repositories.UserRoleRepository;

@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class);

private final UserRepository userRepository;
private final UserRoleRepository userRoleRepository;

@Autowired
public CustomUserDetailsService(UserRepository userRepository, UserRoleRepository userRoleRepository) {
    this.userRepository = userRepository;
    this.userRoleRepository = userRoleRepository;
}


@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findByUsername(username);
    log.info("Username entered: " +  username);
    if(user.equals(null)) {
        log.info("User with username: " + username + " not found.");
        throw new UsernameNotFoundException("No user found with username: " + username);
    } else {
        log.info("User found with username " + user.getUsername() + " and password " + user.getPassword());
        List<String> userRoles = userRoleRepository.findRoleByUsername(username);
        return new CustomUserDetails(user, userRoles);
    }
}


}
package com.sitename;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.orm.jpa.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootApplication
@EnableJpaRepositories(basePackages = "com.sitename.domain.repositories")
@EntityScan(basePackages = "com.sitename.domain.models")
public class WebsiteApplication {

private static final Logger log = LoggerFactory.getLogger(WebsiteApplication.class);

public static void main(String[] args) {
    SpringApplication.run(WebsiteApplication.class, args);
}
}
我的用户存储库:

package com.sitename.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import  org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.slf4j.*;

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {    
     auth.userDetailsService(userDetailsService);
 } 

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .authorizeRequests()
                .antMatchers("/users/logout").authenticated()
                .anyRequest().permitAll()
                .and()
            .formLogin()
                .loginPage("/users/login") 
                .usernameParameter("username").passwordParameter("password")
                .defaultSuccessUrl("/")
                .successHandler(successHandler())
                //.failureHandler(failureHandler())
                .permitAll();

    }

    @Bean
    public AuthenticationSuccessHandler successHandler() {
        log.info("Login success!");
        SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
        handler.setUseReferer(true);
        return handler;
    }

    //    @Bean
    //    public AuthenticationFailureHandler failureHandler() {
    //      log.info("Login failure - bad credentials");
    //      return failureHandler();
    //    }

   @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        log.info("Running configureGlobal method in WebSecurityConfig");
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
//        auth
//            .inMemoryAuthentication()
//                .withUser("user").password("password").roles("USER");
    }

    @Bean(name="passwordEncoder")
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
package com.sitename.domain.models;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;

@Entity
@Table(name = "users")
public class User implements Serializable {


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

@NotBlank
@NotEmpty
@NotNull
@Column(name = "username")
private String username;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "email")
private String email;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "password")
private String password;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "enabled")
private int enabled;

public User(String username, String password, String email, int enabled) {
    this.username = username;
    this.password = password;
    this.email = email;
    this.enabled = enabled;
}

public User(User user) {
    this.id = user.id;
    this.username = user.username;
    this.email = user.email;
    this.password = user.password;
    this.enabled = user.enabled;
}

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

public int getEnabled() {
    return enabled;
}

public void setEnabled(int enabled) {
    this.enabled = enabled;
}


public User() {}

}
package com.sitename.domain.models;

import javax.persistence.*;

@Entity
@Table(name = "user_roles")
public class UserRole {

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

@Column(name = "user_id")
private Long userId;

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

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public Long getUserId() {
    return userId;
}

public void setUserId(Long userId) {
    this.userId = userId;
}

public String getRole() {
    return role;
}

public void setRole(String role) {
    this.role = role;
}

}
package com.sitename.domain.repositories;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.sitename.domain.models.User;

@Repository
public interface UserRepository extends CrudRepository<User, Long> {

public User findByUsername(String username);
}
package com.sitename.domain.repositories;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.sitename.domain.models.UserRole;

import java.util.List;

@Repository
public interface UserRoleRepository extends CrudRepository<UserRole, Long> {

@Query("select a.role from UserRole a, User b where b.username=?1 and a.id=b.id")
public List<String> findRoleByUsername(String username);
 }
package com.sitename.security;

import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import com.sitename.domain.models.User;

public class CustomUserDetails extends User implements UserDetails {

private static final long serialVersionUID = 1L;
private List<String> userRoles;

public CustomUserDetails(User user, List<String> userRoles) {
    super(user);
    this.userRoles = userRoles;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    String roles=StringUtils.collectionToCommaDelimitedString(userRoles);           
    return AuthorityUtils.commaSeparatedStringToAuthorityList(roles);
}

@Override
public String getPassword() {
    return super.getPassword();
}

@Override
public String getUsername() {
    return super.getUsername();
}

@Override
public boolean isAccountNonExpired() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isAccountNonLocked() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isCredentialsNonExpired() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isEnabled() {
    // TODO Auto-generated method stub
    return false;
}

}
package com.sitename.security;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import org.slf4j.*;

import com.sitename.domain.models.User;
import com.sitename.domain.repositories.UserRepository;
import com.sitename.domain.repositories.UserRoleRepository;

@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class);

private final UserRepository userRepository;
private final UserRoleRepository userRoleRepository;

@Autowired
public CustomUserDetailsService(UserRepository userRepository, UserRoleRepository userRoleRepository) {
    this.userRepository = userRepository;
    this.userRoleRepository = userRoleRepository;
}


@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findByUsername(username);
    log.info("Username entered: " +  username);
    if(user.equals(null)) {
        log.info("User with username: " + username + " not found.");
        throw new UsernameNotFoundException("No user found with username: " + username);
    } else {
        log.info("User found with username " + user.getUsername() + " and password " + user.getPassword());
        List<String> userRoles = userRoleRepository.findRoleByUsername(username);
        return new CustomUserDetails(user, userRoles);
    }
}


}
package com.sitename;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.orm.jpa.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootApplication
@EnableJpaRepositories(basePackages = "com.sitename.domain.repositories")
@EntityScan(basePackages = "com.sitename.domain.models")
public class WebsiteApplication {

private static final Logger log = LoggerFactory.getLogger(WebsiteApplication.class);

public static void main(String[] args) {
    SpringApplication.run(WebsiteApplication.class, args);
}
}
主类:

package com.sitename.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import  org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;

import org.slf4j.*;

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {    
     auth.userDetailsService(userDetailsService);
 } 

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
            .authorizeRequests()
                .antMatchers("/users/logout").authenticated()
                .anyRequest().permitAll()
                .and()
            .formLogin()
                .loginPage("/users/login") 
                .usernameParameter("username").passwordParameter("password")
                .defaultSuccessUrl("/")
                .successHandler(successHandler())
                //.failureHandler(failureHandler())
                .permitAll();

    }

    @Bean
    public AuthenticationSuccessHandler successHandler() {
        log.info("Login success!");
        SimpleUrlAuthenticationSuccessHandler handler = new SimpleUrlAuthenticationSuccessHandler();
        handler.setUseReferer(true);
        return handler;
    }

    //    @Bean
    //    public AuthenticationFailureHandler failureHandler() {
    //      log.info("Login failure - bad credentials");
    //      return failureHandler();
    //    }

   @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        log.info("Running configureGlobal method in WebSecurityConfig");
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
//        auth
//            .inMemoryAuthentication()
//                .withUser("user").password("password").roles("USER");
    }

    @Bean(name="passwordEncoder")
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}
package com.sitename.domain.models;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;

import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;

@Entity
@Table(name = "users")
public class User implements Serializable {


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

@NotBlank
@NotEmpty
@NotNull
@Column(name = "username")
private String username;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "email")
private String email;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "password")
private String password;

@NotBlank
@NotEmpty
@NotNull
@Column(name = "enabled")
private int enabled;

public User(String username, String password, String email, int enabled) {
    this.username = username;
    this.password = password;
    this.email = email;
    this.enabled = enabled;
}

public User(User user) {
    this.id = user.id;
    this.username = user.username;
    this.email = user.email;
    this.password = user.password;
    this.enabled = user.enabled;
}

public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

public int getEnabled() {
    return enabled;
}

public void setEnabled(int enabled) {
    this.enabled = enabled;
}


public User() {}

}
package com.sitename.domain.models;

import javax.persistence.*;

@Entity
@Table(name = "user_roles")
public class UserRole {

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

@Column(name = "user_id")
private Long userId;

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

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public Long getUserId() {
    return userId;
}

public void setUserId(Long userId) {
    this.userId = userId;
}

public String getRole() {
    return role;
}

public void setRole(String role) {
    this.role = role;
}

}
package com.sitename.domain.repositories;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.sitename.domain.models.User;

@Repository
public interface UserRepository extends CrudRepository<User, Long> {

public User findByUsername(String username);
}
package com.sitename.domain.repositories;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.sitename.domain.models.UserRole;

import java.util.List;

@Repository
public interface UserRoleRepository extends CrudRepository<UserRole, Long> {

@Query("select a.role from UserRole a, User b where b.username=?1 and a.id=b.id")
public List<String> findRoleByUsername(String username);
 }
package com.sitename.security;

import java.util.Collection;
import java.util.List;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.StringUtils;

import com.sitename.domain.models.User;

public class CustomUserDetails extends User implements UserDetails {

private static final long serialVersionUID = 1L;
private List<String> userRoles;

public CustomUserDetails(User user, List<String> userRoles) {
    super(user);
    this.userRoles = userRoles;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
    String roles=StringUtils.collectionToCommaDelimitedString(userRoles);           
    return AuthorityUtils.commaSeparatedStringToAuthorityList(roles);
}

@Override
public String getPassword() {
    return super.getPassword();
}

@Override
public String getUsername() {
    return super.getUsername();
}

@Override
public boolean isAccountNonExpired() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isAccountNonLocked() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isCredentialsNonExpired() {
    // TODO Auto-generated method stub
    return true;
}

@Override
public boolean isEnabled() {
    // TODO Auto-generated method stub
    return false;
}

}
package com.sitename.security;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import org.slf4j.*;

import com.sitename.domain.models.User;
import com.sitename.domain.repositories.UserRepository;
import com.sitename.domain.repositories.UserRoleRepository;

@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

Logger log = LoggerFactory.getLogger(CustomUserDetailsService.class);

private final UserRepository userRepository;
private final UserRoleRepository userRoleRepository;

@Autowired
public CustomUserDetailsService(UserRepository userRepository, UserRoleRepository userRoleRepository) {
    this.userRepository = userRepository;
    this.userRoleRepository = userRoleRepository;
}


@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userRepository.findByUsername(username);
    log.info("Username entered: " +  username);
    if(user.equals(null)) {
        log.info("User with username: " + username + " not found.");
        throw new UsernameNotFoundException("No user found with username: " + username);
    } else {
        log.info("User found with username " + user.getUsername() + " and password " + user.getPassword());
        List<String> userRoles = userRoleRepository.findRoleByUsername(username);
        return new CustomUserDetails(user, userRoles);
    }
}


}
package com.sitename;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.orm.jpa.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootApplication
@EnableJpaRepositories(basePackages = "com.sitename.domain.repositories")
@EntityScan(basePackages = "com.sitename.domain.models")
public class WebsiteApplication {

private static final Logger log = LoggerFactory.getLogger(WebsiteApplication.class);

public static void main(String[] args) {
    SpringApplication.run(WebsiteApplication.class, args);
}
}
我认为这可能是Hibernate查询的一个错误,因为日志中有“where username=?”部分,而不是where username=“test”,但我不知道HQL,所以我可能是错的

以下是我尝试登录时显示的日志部分:

Hibernate: select user0_.id as id1_1_, user0_.email as email2_1_, user0_.enabled as enabled3_1_, user0_.password as password4_1_, user0_.username as username5_1_ from users user0_ where user0_.username=?
[2m2016-03-12 16:41:37.487[0;39m [32m INFO[0;39m [35m3420[0;39m [2m---[0;39m     [2m[nio-9000-exec-8][0;39m [36mc.d.security.CustomUserDetailsService   [0;39m [2m:[0;39m Username entered: test
[2m2016-03-12 16:41:37.487[0;39m [32m INFO[0;39m [35m3420[0;39m [2m---[0;39m     [2m[nio-9000-exec-8][0;39m [36mc.d.security.CustomUserDetailsService   [0;39m [2m:[0;39m User found with username test and password $2a$10$Lrp4kzL12tC.p0Ut7i92oeZzXn9WCplba1iPoJxAXRysxQ2dwvFxq
Hibernate: select userrole0_.role as col_0_0_ from user_roles userrole0_ cross join users user1_ where user1_.username=? and userrole0_.id=user1_.id
我还注意到successHandler()方法是在启动时运行的(它出现在日志中)-不确定这是否正常

非常感谢您的帮助