是否有可能仅使用 Spring Security 和 Data JPA 来实现加密的数据库支持的登录过程?

问题描述 投票:0回答:2

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

  • 用户在注册页面上注册,所有检查均已完成并 用户已创建

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

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

  • 我的自定义实现

    UserDetailsService
    获取用户名的数据库条目

  • Spring security 比较密码并登录用户

  • 成功登录后,我得到一个主体,并且可以显示
    以此为基础的内容

问题出在加密上。基本上,我找不到一种没有错误的实现方法。无论我尝试什么,该应用程序似乎都没有尝试将登录页面中的密码与数据库中的加密密码进行匹配。根据 Baeldung,只需定义一个编码器并将其添加到 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;
    }
}

我在这里创建了一个post来寻求帮助,因为我的

UserDetailsService
的自动装配一开始不起作用。在那里,我得到的唯一答案是以与教程不同的方式定义编码器:

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

这会更改保存到存储库的密码

$2a$10$TEU8lkr/aVzJMgtu78.NceUzy8zJG5FCqHcOgNK61AL5or0McLpTq

{bcrypt}$2a$10$TEU8lkr/aVzJMgtu78.NceUzy8zJG5FCqHcOgNK61AL5or0McLpTq

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

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

给我编码器新定义的用户将我链接到这个问题,其中有人遇到了同样的问题。这就是导致我以我的方式表达这个问题的原因。

那里接受的答案以及其他一些答案似乎要么使用Oauth2(一个答案使用我什至无法导入的类

ClientDetailsServiceConfigurer
),禁用加密(或者更确切地说使用不执行任何操作的编码器)或使用InMemoryAuthentication 而不是数据库。这些对我来说都没有用,因为我想真正使用它并永久存储我的用户。

当前版本的 Spring Security 是否存在某种问题导致此功能无法正常工作?在我看来,唯一缺少的步骤是比较加密密码和登录输入。之前使用 inMemoryAuth 且没有加密时效果很好。

或者我是否必须使用一些额外的服务,例如 OAuth2(我认为这是 Spring Security 的补充,而不是必需的)?我只是想知道什么有效,然后再花三天时间尝试不同的教程,让它看起来应该很容易工作,然后发现它根本不起作用。

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

首先是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 Config 添加登录视图:

@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");
    }

}

登录.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>

实现UserDetails的User(UserDB没有权限的话基本是一样的):

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

请帮助我。您能告诉我的任何信息都将不胜感激。看起来这应该非常简单,但到目前为止,这对我来说只是令人沮丧。我真的不知道我可能做错了什么,最糟糕的是感觉我真的很接近。

我想到的另一个选择:如果这无法正常工作,我可以自己检查吗?我必须重写什么类和方法才能完成将密码与我自己的逻辑进行比较的简单任务,并告诉 Spring Security“此用户是有效的”?

提前致谢!

java spring spring-boot encryption spring-security
2个回答
2
投票

您的数据库中似乎有

bcrypt
密码,但未标记为
bcrypt
。尝试:

public PasswordEncoder passwordEncoder() {                                      
    DelegatingPasswordEncoder encoder =                                         
        PasswordEncoderFactories.createDelegatingPasswordEncoder();             
                                                                                
    encoder.setDefaultPasswordEncoderForMatches(new BCryptPasswordEncoder());   
                                                                                
    return encoder;                                                             
}

这应该将任何没有“{id}”的密码解释为 bcrypt。

[已更新。]

另一项变化源自。我的代码是向您的

WebSecurityConfigurerAdapter
实现添加一个方法:

public class WebSecurityConfigurerImpl extends WebSecurityConfigurerAdapter {
    ...

    @Autowired private UserDetailsService userDetailsService;
    @Autowired private PasswordEncoder passwordEncoder;

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

    ...
}

希望这有帮助。


0
投票

我没有太多的声誉,但非常感谢你们:D,我花了几个小时试图弄清楚为什么 BCrypt 不是 Bcrypt,结果发现我弄乱了电子邮件和密码的位置

© www.soinside.com 2019 - 2024. All rights reserved.