【发布时间】:2021-12-12 01:37:11
【问题描述】:
我正在使用 kotlin 和 Spring Boot 2.5.5 以及 MySQL 数据库开发登录服务。一切似乎都正常工作(注册用户、删除用户、更新用户),除了当我尝试从邮递员调用登录端点时,我得到一个错误:
2021-10-26 18:16:28.558 WARN 26076 --- [nio-8080-exec-2] o.s.s.c.bcrypt.BCryptPasswordEncoder : Encoded password does not look like BCrypt
我搜索了整个 stackoverflow,发现其他人也有这个问题,但没有一个建议的答案对我有用(在某些情况下甚至适用于我的情况)
我的 SecurityConfig 类:
@Configuration
@EnableWebSecurity
class SecurityConfig(private val dataSource: DataSource) : WebSecurityConfigurerAdapter() {
override fun configure(auth: AuthenticationManagerBuilder) {
auth.inMemoryAuthentication()
.withUser("user").password("password")
.authorities("USER", "ADMIN", "CONTRIBUTOR")
}
override fun configure(http: HttpSecurity?) {
http!!.csrf().disable()
.authorizeRequests()
.antMatchers("/admin/**").hasAuthority("ADMIN")
.antMatchers("/contributor/**").hasAuthority("CONTRIBUTOR")
.antMatchers("/anonymous*").anonymous()
.antMatchers("/login/**").permitAll()
.antMatchers("/login*").permitAll()
.antMatchers("/**").hasAuthority("USER")
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.logout().permitAll()
}
@Autowired
fun configureGlobal(auth: AuthenticationManagerBuilder) {
auth.jdbcAuthentication()
.passwordEncoder(passwordEncoder())
.dataSource(dataSource)
.usersByUsernameQuery("select username,encrypted_password,\'true\' from dragonline.user where username=?")
.authoritiesByUsernameQuery("select user_username,roles_name from dragonline.user_has_roles where user_username=?")
}
}
class AppInitializer : WebApplicationInitializer {
override fun onStartup(servletContext: ServletContext) {
val root = AnnotationConfigWebApplicationContext()
root.register(SecurityConfig::class.java)
servletContext.addListener(ContextLoaderListener(root))
servletContext.addFilter("securityFilter", DelegatingFilterProxy("springSecurityFilterChain"))
.addMappingForUrlPatterns(null, false, "/*")
}
}
我的登录控制器:
@RestController
@RequestMapping(path = ["/login"])
class Login(private val userRegistrationService: UserRegistrationService) {
@PostMapping(path = ["/register"], consumes = [MediaType.APPLICATION_JSON_VALUE])
fun register(@RequestBody user: UserRegistrationDTO, response: HttpServletResponse): UserRegistrationDTO {
user.confirmPassword()
val domainUser = user.toDomain()
userRegistrationService.saveUser(domainUser)
response.status = HttpServletResponse.SC_CREATED
return user.maskPassword()
}
@CrossOrigin
@PostMapping(path = ["/find-user"], consumes = [MediaType.APPLICATION_JSON_VALUE])
fun login(@RequestBody user: UserLoginDTO, response: HttpServletResponse): UserLoginDTO {
val userFound = userRegistrationService.findUser(user.username)
println("The password stored ind DB is ${userFound?.encryptedPassword}")
if (passwordEncoder().matches(userFound?.encryptedPassword, user.password)) {
response.status = HttpServletResponse.SC_OK
response.setHeader(
"Session-ID",
"${passwordEncoder().encode(user.username)}@.@${passwordEncoder().encode(user.password)}"
)
} else {
response.status = HttpServletResponse.SC_NOT_FOUND
}
return user.maskPassword()
}
@GetMapping(path = ["/delete-user/{userId}"])
fun deleteUser(@PathVariable userId: String) {
userRegistrationService.deleteUser(userId)
}
}
我的数据库中的条目如下所示:
'test', '$2a$10$Z/FSxELmMtV2zpgFVYzDi.dprUybnzJxF6f/kZan7DqHfQ9VDjmQq', 'test@test.com', 'Tester', 'Testing a Test', NULL, '0'
这些列分别是用户名、加密密码、电子邮件、姓名、姓氏、会话ID、会话活动
我的用户服务:
@Service
@Transactional
class UserRegistrationService(
private val userRegistrationRepository: UserRegistrationRepository
) {
fun findUser(userId: String) =
userRegistrationRepository.findByUsername(userId) ?: userRegistrationRepository.findByEmail(userId)
fun saveUser(user: UserRegistrationData) =
userRegistrationRepository.save(user.toPersistence())
fun deleteUser(userId: String) {
val user = userRegistrationRepository.findByUsername(userId) ?: userRegistrationRepository.findByEmail(userId)
?: throw Exception("The user can't be deleted, because it doesn't exist")
user.username.let {
userRegistrationRepository.deleteByUsername(it)
}
}
}
我的仓库:
@Repository
interface UserRegistrationRepository: JpaRepository<User, String> {
fun findByUsername(username: String): User?
fun findByEmail(email: String): User?
fun deleteByUsername(username: String): Long
fun deleteByEmail(email: String): Long
}
我的扩展函数文件(我把我的 Eencoder bean 放在这里):
fun UserRegistrationData.toPersistence() = User(
username = this.username,
encryptedPassword = this.encryptedPassword,
email = this.email,
name = this.name,
lastnames = this.lastnames,
roles = this.roles.map { it.toPersistence() }.toHashSet()
)
fun UserRole.toPersistence() = Role(
name = this.name
)
fun UserLoginDTO.maskPassword() = UserLoginDTO(
username = this.username,
password = "***********"
)
fun UserRegistrationDTO.confirmPassword() {
if (this.password != this.confirmPassword) throw ResponseStatusException(
HttpStatus.BAD_REQUEST,
"The passwords don't match"
)
}
fun UserRegistrationDTO.maskPassword() = UserRegistrationDTO(
username = this.username,
password = "***********",
confirmPassword = "***********",
email = this.email,
name = this.name,
lastnames = this.lastnames,
admin = this.admin,
contributor = this.contributor
)
fun UserRegistrationDTO.toDomain(): UserRegistrationData {
val user = UserRegistrationData(
name = this.name,
lastnames = this.lastnames,
email = this.email,
username = this.username,
encryptedPassword = passwordEncoder().encode(this.password),
roles = mutableListOf(UserRole.USER)
)
if(this.admin) user.roles.add(UserRole.ADMIN)
if(this.contributor) user.roles.add(UserRole.CONTRIBUTOR)
return user
}
@Bean
fun passwordEncoder(): PasswordEncoder {
return BCryptPasswordEncoder()
}
我的模型(它们不都在同一个包中,这就是为什么有这么多):
data class UserRegistrationDTO(
val name: String? = null,
val lastnames: String? = null,
@field:NotBlank
val username: String,
@field:Email
val email: String,
@field:NotBlank
val password: String,
@field:NotBlank
val confirmPassword: String,
val admin: Boolean = false,
val contributor: Boolean = false
)
data class UserLoginDTO(
val username: String,
val password: String
)
data class UserRegistrationData(
val name: String? = null,
val lastnames: String? = null,
@field:NotBlank
val username: String,
@field:Email
val email: String,
@field:NotBlank
val encryptedPassword: String,
val roles: MutableList<UserRole> = mutableListOf(UserRole.USER)
)
class User(
@Id
var username: String,
@Column
var email: String,
@Column
var name: String?,
@Column
var lastnames: String?,
@Column
var encryptedPassword: String,
@OneToMany
@JoinTable(
name = "user_has_roles",
joinColumns = [JoinColumn(name = "user_username")],
inverseJoinColumns = [JoinColumn(name = "roles_name")]
)
var roles: Set<Role>? = null
)
最后是我的Postman Request
如果您需要更多信息,请告诉我。
【问题讨论】:
-
我是。在我的扩展函数中执行 UserRegistrationDTO.toDomain() 时,我正在加密密码:
encryptedPassword = passwordEncoder().encode(this.password)也许我应该发布我的存储库以获得整个路径。我要编辑以包含它 -
请不要在 cmets 中写,使用“编辑”按钮更新您的问题。
-
我不知道您为什么要编写所有这些自定义内容,但是您可以做几件事,例如调试您的应用程序。我相信你使用的api错误
matches(CharSequence rawPassword, String encodedPassword)你需要切换2个输入参数 -
@Toerktumlare 我写这些都是为了学习和练习:)。谢谢!就是这样......我不敢相信我错过了......
标签: mysql spring-boot kotlin spring-security bcrypt