简体   繁体   English

Spring Boot / Spring Security,登录表单,密码检查

[英]Spring Boot/ Spring Security, login form, password checking

I have a problem with something that is probably very easy, but I don't understand that. 我有一个问题可能很容易,但我不明白。

I am not very familiar with Spring Boot, many things happen here automatically. 我对Spring Boot不是很熟悉,很多事情都是自动发生的。 I want to check if someone who write username and password in form is present in database [and his account is activated]. 我想检查是否有人在表单中写入用户名和密码[并且他的帐户已激活]。 User data is stored in MySQL database configured in application.properties. 用户数据存储在application.properties中配置的MySQL数据库中。 I want to check if someone with provided username is present in "user" table and check if provided password is equal to user password in database. 我想检查“user”表中是否存在提供用户名的人,并检查提供的密码是否等于数据库中的用户密码。 At the moment I can type any username from database and password can be random (it's obvious to me, because I don't check it anywhere, and weird, because I feel like everything around says it works properly). 目前我可以从数据库中输入任何用户名,密码可以是随机的(这对我来说很明显,因为我不在任何地方检查它,而且很奇怪,因为我觉得周围的一切都说它工作正常)。 This sounds so simple to me, but I can't find any proper solution on StackOverflow or tutorials. 这对我来说听起来很简单,但我在StackOverflow或教程上找不到任何合适的解决方案。

My general question is - where and how should I check the password from the login form? 我的一般问题是 - 我应该在何处以及如何从登录表单中检查密码? Is it done automatically (but it doesn't work somehow), or should I write my custom controller /service/method to do that? 它是自动完成的(但它不能以某种方式工作),或者我应该编写我的自定义控制器 /服务/方法来做到这一点? If custom controller is needed, then what should be my direction to solve my problem? 如果需要自定义控制器,那么解决问题的方向应该是什么?

At the moment I have no idea where to go. 目前我不知道该往哪里去。 I hope all remaining code related to my problem is pasted here. 我希望所有与我的问题相关的剩余代码都粘贴在这里。 Thank you in advance for all tips and comments about that. 提前感谢您提供有关该提示的所有提示和评论。

Code: 码:

ApplicationSecurityAdapter class: ApplicationSecurityAdapter类:

@Configuration
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class ApplicationSecurityAdapter extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/user/register").permitAll()
            .antMatchers("/user/activate").permitAll()
            .antMatchers("/user/activation-send").permitAll()
            .antMatchers("/user/reset-password").permitAll()
            .antMatchers("/user/reset-password-change").permitAll()
            .antMatchers("/user/autologin").access("hasRole('ROLE_ADMIN')")
            .antMatchers("/user/delete").access("hasRole('ROLE_ADMIN')")
            .antMatchers("/img/**").permitAll()
            .antMatchers("/images/**").permitAll()
            .antMatchers("/fonts/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
            .and()
            .logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")).logoutSuccessUrl("/login").permitAll() // added permitAll()
            .and()
            .rememberMe().key(applicationSecret)
            .tokenValiditySeconds(31536000);
    }

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(new BCryptPasswordEncoder());
}

UserService class: UserService类:

@Service
public class UserService implements UserDetailsService {

    @Value("${app.user.verification}") // set to YES
    private Boolean requireActivation;

    @Value("${app.secret}") // some random stuff
    private String applicationSecret;

    @Autowired
    private UserRepository repo;

    @Autowired
    private HttpSession httpSession;

    public final String CURRENT_USER_KEY = "CURRENT_USER";

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = repo.findOneByUserName(username);

        if(user == null) {
            throw new UsernameNotFoundException(username);
        }
        if(requireActivation && !user.getToken().equals("1")) {
            Application.log.error("User [" + username + "] tried to log in, but his account is not activated.");
            throw new UsernameNotFoundException(username + " did not activate his account.");
        }
        httpSession.setAttribute(CURRENT_USER_KEY, user);
        List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole());

        return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), auth);
    }
}

UserController: UserController的:

@Controller
// @RequestMapping("/user/*")
public class UserController {
    private Logger log = LoggerFactory.getLogger(UserController.class);

    @Value("${app.user.verification}") // YES
    private Boolean requireActivation;

    @Value("users/")
    private String userRoot;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    protected AuthenticationManager authenticationManager;

    @Autowired
    private UserService userService;

    @RequestMapping("/login")
    public String login(User user) {
        return "user/login";
    }
}

Login form: 登录表格:

<div layout:fragment="content">

    <form class="form-signin" th:action="@{/login}" th:object="${user}" method="post">
        <h2 class="form-signin-heading">LOGIN PANEL</h2>
        <div class="alert alert-danger" th:if="${param.error}">
            Incorrect credentials or account not activated.
        </div>
        <input type="text" id="inputUsername" name="username" class="form-control top" placeholder="username goes here..." required="required" autofocus="autofocus"/>
        <input type="password" id="inputPassword" name="password" class="form-control bottom" placeholder="password goes here..."
           required="required"/>
        <div class="checkbox">
            <label>
                <input type="checkbox" name="remember-me"/> Remember me
            </label>
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Log in</button>
    </form>
</div>

The problem is in your loadUserByUsername 问题出在loadUserByUsername

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = repo.findOneByUserName(username);

    if(user == null) {
        throw new UsernameNotFoundException(username);
    }
    if(requireActivation && !user.getToken().equals("1")) {
        Application.log.error("User [" + username + "] tried to log in, but his account is not activated.");
        throw new UsernameNotFoundException(username + " did not activate his account.");
    }
    httpSession.setAttribute(CURRENT_USER_KEY, user);
    List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole());

    return new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), auth);
}

You set the user to the session. 您将用户设置为会话。 Don't do this! 不要这样做! Just load the user and return it. 只需加载用户并将其返回。 The user is stored in the session automatically and can be looked up like shown in this answer . 用户自动存储在会话中,可以像在此答案中所示查找。 I think the reason why the password check doesn't work is that you configured BCryptPasswordEncoder as password encoder. 我认为密码检查不起作用的原因是您将BCryptPasswordEncoder配置为密码编码器。 Be sure, that the password, you have stored in your User , is encoded by this encoder. 请确保您存储在User中的密码由此编码器进行编码。 Otherwise the password check will fail. 否则密码检查将失败。 To avoid the custom activation checking make your User class implement UserDetails . 要避免自定义激活检查,请使User类实现UserDetails If you check the docs there are 4 flags you can set, which will be checked by spring boot. 如果您检查文档,则可以设置4个标志,将由spring boot检查。

boolean isAccountNonExpired() // Indicates whether the user's account has expired.
boolean isAccountNonLocked() // Indicates whether the user is locked or unlocked.
boolean isCredentialsNonExpired() // Indicates whether the user's credentials (password) has expired.
boolean isEnabled() // Indicates whether the user is enabled or disabled.

Your implementation of loadUserByUsername should look something like this. 您的loadUserByUsername实现应该如下所示。 It really should only do what the method name suggests. 它真的应该只做方法名称建议。 Lookup the user and throw a UsernameNotFoundException if you cant find a user with the given username. 如果您找不到具有给定用户名的用户,请查找用户并抛出UsernameNotFoundException

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  User user = repo.findOneByUserName(username);

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

  return user;
}

If you don't want to make your 'User' implement 'UserDetails' (to separate the framework and your business logic for example) return the Spring User using this constructor , where you can set these flags. 如果您不想让'User'实现'UserDetails'(例如,将框架和业务逻辑分开),请使用此构造函数返回Spring 用户 ,您可以在其中设置这些标志。 Your implementation could look like this: 您的实现可能如下所示:

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  User user = repo.findOneByUserName(username);

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

  List<GrantedAuthority> auth = AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRole());
  return new org.springframework.security.core.userdetails.User(
    user.getUserName(),
    user.getPassword(),
    requireActivation && !user.getToken().equals("1"), // enabled. Use whatever condition you like
    true, // accountNonExpired. Use whatever condition you like
    true, // credentialsNonExpired. Use whatever condition you like
    true, // accountNonLocked. Use whatever condition you like
    auth);
}

The password, authorities, activation state and so on are then checked by spring automatically. 然后,弹簧自动检查密码,权限,激活状态等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM