Java 甚至可以只使用Spring Security和Data JPA,通过加密实现数据库支持的登录过程吗?

Java 甚至可以只使用Spring Security和Data JPA,通过加密实现数据库支持的登录过程吗?,java,spring,spring-boot,encryption,spring-security,Java,Spring,Spring Boot,Encryption,Spring Security,正如标题所述,我正在尝试实现一些东西,在这一点上,我甚至不确定是否可能以我想象的方式实现它。我想要一个非常简单的、数据库支持的注册和登录过程,以便向不同的用户显示不同的页面内容。所以它应该是这样工作的: 用户在注册页面上注册,执行所有检查并 用户已创建 密码用Bcrypt加密,用户名和密码为 使用Spring数据JPA存储在数据库中 用户通过“标准”Spring安全登录表单登录 UserDetailsService的自定义实现获取用户名的数据库条目 Spring security比较用

正如标题所述,我正在尝试实现一些东西,在这一点上,我甚至不确定是否可能以我想象的方式实现它。我想要一个非常简单的、数据库支持的注册和登录过程,以便向不同的用户显示不同的页面内容。所以它应该是这样工作的:

  • 用户在注册页面上注册,执行所有检查并 用户已创建

  • 密码用Bcrypt加密,用户名和密码为
    使用Spring数据JPA存储在数据库中

  • 用户通过“标准”Spring安全登录表单登录

  • UserDetailsService
    的自定义实现获取用户名的数据库条目

  • Spring security比较用户的密码和日志

  • 成功登录后,我将获得一个主体,并能够显示
    基于此的内容

问题在于加密。基本上,我无法找到一种不出错的方法来实现它。无论我尝试了什么,应用程序似乎都没有尝试将登录页面中的密码与数据库中的加密密码进行匹配。根据,仅定义编码器并将其添加到AuthenticationProvider就足够了。然而,当我遵循本教程时,我得到一个错误,即

编码的密码看起来不像bcrypt

这是我的
UserDetailsService
,如注释中所述,密码实际上是一个有效的60个字符的Bcrypt

package com.example.demo;

import...

import java.util.Optional;

@Service("myUserDetailsService")
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        Optional<UserDB> userDB = userRepository.findById(s);

        User user = null;

        if (userDB.isPresent()) {
            System.out.println("present!");
            UserDB userDB2 = userDB.get();
            System.out.println(userDB2.getPassword());
            // The line above prints a valid BCrypt password with 60 characters
            user = new User(userDB2.getUsername(), userDB2.getPassword());
        }


        return user;
    }
}
这将更改从保存到存储库的密码

$2a$10$TEU8lkr/aVzJMgtu78.NceUzy8zJG5FCqHcOgNK61AL5or0McLpTq

但是我得到了一种新的错误

java.lang.IllegalArgumentException:没有为id“null”映射的PasswordEncoder

给我编码器的新定义的用户将我链接到,有人也有同样的问题。这就是我用我的方式来表达这个问题的原因

这里接受的答案,以及其他一些答案,似乎要么使用Oauth2(一个答案使用我甚至无法导入的类
ClientDetailsServiceConfigurer
),禁用加密(或者更确切地说使用一个不做任何事情的编码器),要么使用InMemoryAuthentication而不是数据库。所有这些对我都没有用处,因为我想真正使用它并永久存储我的用户

在当前版本的Spring Security中是否存在某种问题阻止了它的工作?在我看来,唯一缺少的步骤是比较加密密码和登录输入。它在使用inMemoryAuth之前运行良好,没有加密

或者我必须使用一些额外的服务,比如OAuth2(我认为这是对Spring安全性的补充,而不是要求)?我只是想知道什么是有效的,然后我再花三天时间尝试不同的教程,让它看起来应该很容易,然后结果它不知怎么地根本不起作用

我将向您展示我的代码的所有其他相关部分,也许我只是犯了一些非常简单的错误:

首先是WebSecurityConfig

package com.example.demo;

import...

@Configuration
@ComponentScan(basePackages = { "com.example.demo" })
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/", "/main", "/register").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
                .logout()
                .permitAll();
    }

    @Autowired
    private MyUserDetailsService userDetailsService;

@Bean(name = "passwordEncoder")
    @Qualifier("passwordEncoder")
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider authProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }
}
注册的控制器

@PostMapping("/register")
    public String workRegistrationForm(Model model, @ModelAttribute("user") UserDto newUser) {
        System.out.println(newUser.getUsername() + ", " + newUser.getEmail());
        String encodedPW = encoder.encode(newUser.getPassword());
        UserDB newUserDB = new UserDB(newUser.getUsername(), encodedPW);
        userRepository.save(newUserDB);
        return "redirect:/main";
    }
添加登录视图的MVC配置:

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/").setViewName("home");
        registry.addViewController("/securedtwo").setViewName("securedtwo");
        registry.addViewController("/login").setViewName("login");
    }

}
login.html:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
    Invalid username and password.
</div>
<div th:if="${param.logout}">
    You have been logged out.
</div>
<form th:action="@{/login}" method="post">
    <div><label> User Name : <input type="text" name="username"/> </label></div>
    <div><label> Password: <input type="password" name="password"/> </label></div>
    <div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>

Spring安全示例
无效的用户名和密码。
您已注销。
用户名:
密码:
实现UserDetails的用户(UserDB在没有权限的情况下基本相同):

public类用户实现UserDetails{
字符串密码;
字符串用户名;
公共用户(字符串密码、字符串用户名){
this.password=密码;
this.username=用户名;
}
@凌驾

公共集合您的数据库中似乎有
bcrypt
密码未标记为
bcrypt
。请尝试:

公共密码编码器PasswordEncoder(){
DelegatingPasswordEncoder编码器=
PasswordEncoderFactorys.createDelegatingPasswordEncoder();
setDefaultPasswordEncoderForMatches(新的BCryptPasswordEncoder());
返回编码器;
}
这应该将没有“{id}”的任何密码解释为bcrypt

[更新。]

由.my code驱动的另一个更改是将方法添加到
WebSecurityConfigureAdapter
实现中:

公共类WebSecurityConfigureImpl扩展了WebSecurityConfigureAdapter{
...
@自动连线专用用户详细信息服务用户详细信息服务;
@自动连线专用密码编码器;
@凌驾
public void configure(AuthenticationManagerBuilder auth)引发异常{
auth.userDetailsService(userDetailsService)
.密码编码器(passwordEncoder);
}
...
}

希望这能有所帮助。

很遗憾,这不起作用,我得到了错误“密码看起来不像Bcrypt”再一次。你知道我的错误发生在流程的哪一部分吗?是登录密码看起来不像Bcrypt,还是数据库中的密码?错误消息对我没有什么帮助。有一些奇怪的事情发生了,我想知道密码比较问题是否有误导性。在再次查看你的代码后,t我看到的另一个问题是您的
userdetails服务。如果找不到用户,loadUserByUsername(String)
需要抛出
UsernameNotFoundException
(而不是只返回
null
)。我建议添加该代码以查看上游是否发现问题
@Configuration
public class MvcConfig implements WebMvcConfigurer {

    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/").setViewName("home");
        registry.addViewController("/securedtwo").setViewName("securedtwo");
        registry.addViewController("/login").setViewName("login");
    }

}
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
    <title>Spring Security Example </title>
</head>
<body>
<div th:if="${param.error}">
    Invalid username and password.
</div>
<div th:if="${param.logout}">
    You have been logged out.
</div>
<form th:action="@{/login}" method="post">
    <div><label> User Name : <input type="text" name="username"/> </label></div>
    <div><label> Password: <input type="password" name="password"/> </label></div>
    <div><input type="submit" value="Sign In"/></div>
</form>
</body>
</html>
public class User implements UserDetails {

    String password;
    String username;

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


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

        ArrayList<GrantedAuthority> rights = new ArrayList<>();
        rights.add(new SimpleGrantedAuthority("USER"));

        return rights;
    }

..Getters and Setters