feat: implement user credential management with new UserCredential entity and related components
This commit is contained in:
@@ -48,12 +48,6 @@ public class User {
|
||||
@Column(nullable = false, length = 64)
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* The encrypted password for user authentication.
|
||||
*/
|
||||
@Column
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* The user's complete full name.
|
||||
*/
|
||||
@@ -124,10 +118,9 @@ public class User {
|
||||
public User() {
|
||||
}
|
||||
|
||||
public User(Long id, String username, String password, String fullName, String email, String regionAbbreviation, String phoneNumber, String avatarUrl, UserStatus status, Long departmentId, Long positionId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
public User(Long id, String username, String fullName, String email, String regionAbbreviation, String phoneNumber, String avatarUrl, UserStatus status, Long departmentId, Long positionId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = id;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.fullName = fullName;
|
||||
this.email = email;
|
||||
this.regionAbbreviation = regionAbbreviation;
|
||||
@@ -176,24 +169,6 @@ public class User {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the encrypted password.
|
||||
*
|
||||
* @return the encrypted password
|
||||
*/
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the encrypted password.
|
||||
*
|
||||
* @param password the encrypted password (never plain text)
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getFullName() {
|
||||
return fullName;
|
||||
}
|
||||
@@ -337,7 +312,6 @@ public class User {
|
||||
public static class UserBuilder {
|
||||
private Long id;
|
||||
private String username;
|
||||
private String password;
|
||||
private String fullName;
|
||||
private String email;
|
||||
private String regionAbbreviation;
|
||||
@@ -362,11 +336,6 @@ public class User {
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder password(String password) {
|
||||
this.password = password;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserBuilder fullName(String fullName) {
|
||||
this.fullName = fullName;
|
||||
return this;
|
||||
@@ -423,7 +392,7 @@ public class User {
|
||||
* @return a new User instance
|
||||
*/
|
||||
public User build() {
|
||||
return new User(id, username, password, fullName, email, regionAbbreviation, phoneNumber, avatarUrl, status, departmentId, positionId, createdAt, updatedAt);
|
||||
return new User(id, username, fullName, email, regionAbbreviation, phoneNumber, avatarUrl, status, departmentId, positionId, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+25
-25
@@ -1,7 +1,7 @@
|
||||
package com.onixbyte.helix.domain.entity;
|
||||
|
||||
import com.onixbyte.helix.enumeration.IdentityProvider;
|
||||
import com.onixbyte.helix.domain.entity.embeddable.UserIdentityId;
|
||||
import com.onixbyte.helix.enumeration.CredentialProvider;
|
||||
import com.onixbyte.helix.domain.entity.embeddable.UserCredentialId;
|
||||
import jakarta.persistence.*; // 导入 Jakarta Persistence API
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
@@ -19,14 +19,14 @@ import java.util.Objects;
|
||||
* @since 1.0
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "user_identities")
|
||||
public class UserIdentity {
|
||||
@Table(name = "user_credentials")
|
||||
public class UserCredential {
|
||||
|
||||
/**
|
||||
* The composite primary key for the entity, composed of userId, provider, and externalId.
|
||||
*/
|
||||
@EmbeddedId
|
||||
private UserIdentityId id;
|
||||
private UserCredentialId id;
|
||||
|
||||
/**
|
||||
* The timestamp when this identity mapping was created.
|
||||
@@ -53,7 +53,7 @@ public class UserIdentity {
|
||||
* @param userId the user ID
|
||||
*/
|
||||
public void setUserId(Long userId) {
|
||||
if (this.id == null) this.id = new UserIdentityId();
|
||||
if (this.id == null) this.id = new UserCredentialId();
|
||||
this.id.setUserId(userId);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public class UserIdentity {
|
||||
* Gets the external identity provider from the composite primary key.
|
||||
* @return the provider
|
||||
*/
|
||||
public IdentityProvider getProvider() {
|
||||
public CredentialProvider getProvider() {
|
||||
return this.id != null ? this.id.getProvider() : null;
|
||||
}
|
||||
|
||||
@@ -69,8 +69,8 @@ public class UserIdentity {
|
||||
* Sets the external identity provider within the composite primary key.
|
||||
* @param provider the provider
|
||||
*/
|
||||
public void setProvider(IdentityProvider provider) {
|
||||
if (this.id == null) this.id = new UserIdentityId();
|
||||
public void setProvider(CredentialProvider provider) {
|
||||
if (this.id == null) this.id = new UserCredentialId();
|
||||
this.id.setProvider(provider);
|
||||
}
|
||||
|
||||
@@ -78,17 +78,17 @@ public class UserIdentity {
|
||||
* Gets the unique identifier from the external provider from the composite primary key.
|
||||
* @return the external ID
|
||||
*/
|
||||
public String getExternalId() {
|
||||
return this.id != null ? this.id.getExternalId() : null;
|
||||
public String getCredential() {
|
||||
return this.id != null ? this.id.getCredential() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the unique identifier from the external provider within the composite primary key.
|
||||
* @param externalId the external ID
|
||||
* @param credential the external ID
|
||||
*/
|
||||
public void setExternalId(String externalId) {
|
||||
if (this.id == null) this.id = new UserIdentityId();
|
||||
this.id.setExternalId(externalId);
|
||||
public void setCredential(String credential) {
|
||||
if (this.id == null) this.id = new UserCredentialId();
|
||||
this.id.setCredential(credential);
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
@@ -109,12 +109,12 @@ public class UserIdentity {
|
||||
|
||||
// --- Constructors (Adjusted for EmbeddedId) ---
|
||||
|
||||
public UserIdentity() {
|
||||
this.id = new UserIdentityId(); // Initialize ID object for safety
|
||||
public UserCredential() {
|
||||
this.id = new UserCredentialId(); // Initialize ID object for safety
|
||||
}
|
||||
|
||||
public UserIdentity(Long userId, IdentityProvider provider, String externalId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = new UserIdentityId(userId, provider, externalId);
|
||||
public UserCredential(Long userId, CredentialProvider provider, String externalId, LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.id = new UserCredentialId(userId, provider, externalId);
|
||||
this.createdAt = createdAt;
|
||||
this.updatedAt = updatedAt;
|
||||
}
|
||||
@@ -125,7 +125,7 @@ public class UserIdentity {
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UserIdentity that = (UserIdentity) o;
|
||||
UserCredential that = (UserCredential) o;
|
||||
return Objects.equals(id, that.id); // Entity equality based on primary key
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ public class UserIdentity {
|
||||
return "UserIdentity{" +
|
||||
"userId=" + getUserId() +
|
||||
", provider=" + getProvider() +
|
||||
", externalId='" + getExternalId() + '\'' +
|
||||
", externalId='" + getCredential() + '\'' +
|
||||
", createdAt=" + createdAt +
|
||||
", updatedAt=" + updatedAt +
|
||||
'}';
|
||||
@@ -162,7 +162,7 @@ public class UserIdentity {
|
||||
*/
|
||||
public static class UserIdentityBuilder {
|
||||
private Long userId;
|
||||
private IdentityProvider provider;
|
||||
private CredentialProvider provider;
|
||||
private String externalId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
@@ -175,7 +175,7 @@ public class UserIdentity {
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityBuilder provider(IdentityProvider provider) {
|
||||
public UserIdentityBuilder provider(CredentialProvider provider) {
|
||||
this.provider = provider;
|
||||
return this;
|
||||
}
|
||||
@@ -200,8 +200,8 @@ public class UserIdentity {
|
||||
*
|
||||
* @return a new UserIdentity instance
|
||||
*/
|
||||
public UserIdentity build() {
|
||||
return new UserIdentity(userId, provider, externalId, createdAt, updatedAt);
|
||||
public UserCredential build() {
|
||||
return new UserCredential(userId, provider, externalId, createdAt, updatedAt);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-16
@@ -1,6 +1,6 @@
|
||||
package com.onixbyte.helix.domain.entity.embeddable;
|
||||
|
||||
import com.onixbyte.helix.enumeration.IdentityProvider;
|
||||
import com.onixbyte.helix.enumeration.CredentialProvider;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import jakarta.persistence.Enumerated;
|
||||
@@ -18,7 +18,7 @@ import java.util.Objects;
|
||||
* from that provider.
|
||||
*/
|
||||
@Embeddable
|
||||
public class UserIdentityId implements Serializable {
|
||||
public class UserCredentialId implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
@@ -37,23 +37,23 @@ public class UserIdentityId implements Serializable {
|
||||
@Column(nullable = false)
|
||||
@Enumerated
|
||||
@JdbcType(PostgreSQLEnumJdbcType.class)
|
||||
private IdentityProvider provider;
|
||||
private CredentialProvider provider;
|
||||
|
||||
/**
|
||||
* The unique identifier from the external provider, corresponding to the 'external_id' column.
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private String externalId;
|
||||
private String credential;
|
||||
|
||||
// --- Constructors ---
|
||||
|
||||
public UserIdentityId() {
|
||||
public UserCredentialId() {
|
||||
}
|
||||
|
||||
public UserIdentityId(Long userId, IdentityProvider provider, String externalId) {
|
||||
public UserCredentialId(Long userId, CredentialProvider provider, String credential) {
|
||||
this.userId = userId;
|
||||
this.provider = provider;
|
||||
this.externalId = externalId;
|
||||
this.credential = credential;
|
||||
}
|
||||
|
||||
// --- Getters and Setters (Omitted for brevity, but should exist) ---
|
||||
@@ -65,32 +65,32 @@ public class UserIdentityId implements Serializable {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public IdentityProvider getProvider() {
|
||||
public CredentialProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public void setProvider(IdentityProvider provider) {
|
||||
public void setProvider(CredentialProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public String getExternalId() {
|
||||
return externalId;
|
||||
public String getCredential() {
|
||||
return credential;
|
||||
}
|
||||
|
||||
public void setExternalId(String externalId) {
|
||||
this.externalId = externalId;
|
||||
public void setCredential(String credential) {
|
||||
this.credential = credential;
|
||||
}
|
||||
|
||||
// --- equals and hashCode (REQUIRED for composite keys) ---
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
UserIdentityId that = (UserIdentityId) o;
|
||||
return Objects.equals(userId, that.userId) && provider == that.provider && Objects.equals(externalId, that.externalId);
|
||||
UserCredentialId that = (UserCredentialId) o;
|
||||
return Objects.equals(userId, that.userId) && provider == that.provider && Objects.equals(credential, that.credential);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(userId, provider, externalId);
|
||||
return Objects.hash(userId, provider, credential);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.onixbyte.helix.domain.view;
|
||||
|
||||
import com.onixbyte.helix.enumeration.IdentityProvider;
|
||||
import com.onixbyte.helix.domain.entity.UserIdentity;
|
||||
import com.onixbyte.helix.enumeration.CredentialProvider;
|
||||
import com.onixbyte.helix.domain.entity.UserCredential;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
@@ -26,7 +26,7 @@ public class UserIdentityView {
|
||||
/**
|
||||
* The external identity provider.
|
||||
*/
|
||||
private IdentityProvider provider;
|
||||
private CredentialProvider provider;
|
||||
|
||||
/**
|
||||
* The unique identifier from the external provider.
|
||||
@@ -52,8 +52,8 @@ public class UserIdentityView {
|
||||
/**
|
||||
* Constructor with all fields.
|
||||
*/
|
||||
public UserIdentityView(Long userId, IdentityProvider provider, String externalId,
|
||||
LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
public UserIdentityView(Long userId, CredentialProvider provider, String externalId,
|
||||
LocalDateTime createdAt, LocalDateTime updatedAt) {
|
||||
this.userId = userId;
|
||||
this.provider = provider;
|
||||
this.externalId = externalId;
|
||||
@@ -64,19 +64,19 @@ public class UserIdentityView {
|
||||
/**
|
||||
* Creates a UserIdentityView from a UserIdentity entity.
|
||||
*
|
||||
* @param userIdentity the UserIdentity entity
|
||||
* @param userCredential the UserIdentity entity
|
||||
* @return the UserIdentityView object
|
||||
*/
|
||||
public static UserIdentityView fromEntity(UserIdentity userIdentity) {
|
||||
if (userIdentity == null) {
|
||||
public static UserIdentityView fromEntity(UserCredential userCredential) {
|
||||
if (userCredential == null) {
|
||||
return null;
|
||||
}
|
||||
return new UserIdentityView(
|
||||
userIdentity.getUserId(),
|
||||
userIdentity.getProvider(),
|
||||
userIdentity.getExternalId(),
|
||||
userIdentity.getCreatedAt(),
|
||||
userIdentity.getUpdatedAt()
|
||||
userCredential.getUserId(),
|
||||
userCredential.getProvider(),
|
||||
userCredential.getCredential(),
|
||||
userCredential.getCreatedAt(),
|
||||
userCredential.getUpdatedAt()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -88,11 +88,11 @@ public class UserIdentityView {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public IdentityProvider getProvider() {
|
||||
public CredentialProvider getProvider() {
|
||||
return provider;
|
||||
}
|
||||
|
||||
public void setProvider(IdentityProvider provider) {
|
||||
public void setProvider(CredentialProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ public class UserIdentityView {
|
||||
*/
|
||||
public static class UserIdentityViewBuilder {
|
||||
private Long userId;
|
||||
private IdentityProvider provider;
|
||||
private CredentialProvider provider;
|
||||
private String externalId;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
@@ -175,7 +175,7 @@ public class UserIdentityView {
|
||||
return this;
|
||||
}
|
||||
|
||||
public UserIdentityViewBuilder provider(IdentityProvider provider) {
|
||||
public UserIdentityViewBuilder provider(CredentialProvider provider) {
|
||||
this.provider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -6,8 +6,4 @@ public record LoginSuccessResponse(
|
||||
String accessToken,
|
||||
User user
|
||||
) {
|
||||
|
||||
public LoginSuccessResponse {
|
||||
user.setPassword(null);
|
||||
}
|
||||
}
|
||||
|
||||
+23
-1
@@ -1,3 +1,25 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 OnixByte
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.onixbyte.helix.enumeration;
|
||||
|
||||
import com.onixbyte.helix.config.AuthenticationConfig;
|
||||
@@ -18,7 +40,7 @@ import com.onixbyte.helix.config.AuthenticationConfig;
|
||||
* @since 1.0.0
|
||||
* @see AuthenticationConfig
|
||||
*/
|
||||
public enum IdentityProvider {
|
||||
public enum CredentialProvider {
|
||||
|
||||
/**
|
||||
* Local identity provider using the application's internal user database.
|
||||
@@ -67,8 +67,6 @@ public class TokenAuthenticationFilter extends OncePerRequestFilter {
|
||||
.map((authority) -> (GrantedAuthority) authority::getCode)
|
||||
.toList();
|
||||
|
||||
user.setPassword(null);
|
||||
|
||||
var authentication = UsernamePasswordAuthentication.authenticated(user, authorities);
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
filterChain.doFilter(request, response);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.onixbyte.helix.manager;
|
||||
|
||||
import com.onixbyte.helix.common.regex.Patterns;
|
||||
import com.onixbyte.helix.domain.web.request.ResetPasswordRequest;
|
||||
import com.onixbyte.helix.mapper.UserCredentialMapper;
|
||||
import com.onixbyte.helix.shared.CacheName;
|
||||
import com.onixbyte.helix.domain.database.query.wrapper.QueryUserWrapper;
|
||||
import com.onixbyte.helix.domain.entity.User;
|
||||
@@ -30,16 +32,19 @@ public class UserManager {
|
||||
private final UserMapper userMapper;
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final UserCredentialMapper userCredentialMapper;
|
||||
|
||||
@Autowired
|
||||
public UserManager(
|
||||
UserMapper userMapper,
|
||||
UserRepository userRepository,
|
||||
PasswordEncoder passwordEncoder
|
||||
PasswordEncoder passwordEncoder,
|
||||
UserCredentialMapper userCredentialMapper
|
||||
) {
|
||||
this.userMapper = userMapper;
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.userCredentialMapper = userCredentialMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,11 +92,6 @@ public class UserManager {
|
||||
var userToUpdate = userRepository.findById(user.getId())
|
||||
.orElseThrow(() -> new BizException(HttpStatus.BAD_REQUEST, "找不到 ID 为" + user.getId() + "的用户信息"));
|
||||
|
||||
Optional.ofNullable(user.getPassword())
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.map(passwordEncoder::encode)
|
||||
.ifPresent(userToUpdate::setPassword);
|
||||
|
||||
Optional.ofNullable(user.getFullName())
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.ifPresent(userToUpdate::setFullName);
|
||||
@@ -126,4 +126,12 @@ public class UserManager {
|
||||
|
||||
return userToUpdate;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
public void updateUserPassword(ResetPasswordRequest request) {
|
||||
userCredentialMapper.updateUserCredential(
|
||||
request.id(),
|
||||
passwordEncoder.encode(request.password())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 OnixByte
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package com.onixbyte.helix.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserCredentialMapper {
|
||||
|
||||
int updateUserCredential(
|
||||
@Param("userId") Long userId,
|
||||
@Param("encodedPassword") String encodedPassword
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.onixbyte.helix.repository;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.UserCredential;
|
||||
import com.onixbyte.helix.domain.entity.embeddable.UserCredentialId;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserCredentialRepository extends JpaRepository<UserCredential, UserCredentialId> {
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.onixbyte.helix.repository;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.UserIdentity;
|
||||
import com.onixbyte.helix.domain.entity.embeddable.UserIdentityId;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserIdentityRepository extends JpaRepository<UserIdentity, UserIdentityId> {
|
||||
}
|
||||
+16
-3
@@ -1,13 +1,17 @@
|
||||
package com.onixbyte.helix.security.provider;
|
||||
|
||||
import com.onixbyte.helix.domain.entity.Authority;
|
||||
import com.onixbyte.helix.domain.entity.UserCredential;
|
||||
import com.onixbyte.helix.enumeration.CredentialProvider;
|
||||
import com.onixbyte.helix.exception.BizException;
|
||||
import com.onixbyte.helix.manager.AuthorityManager;
|
||||
import com.onixbyte.helix.manager.UserManager;
|
||||
import com.onixbyte.helix.repository.UserCredentialRepository;
|
||||
import com.onixbyte.helix.security.authentication.UsernamePasswordAuthentication;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Example;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -24,16 +28,19 @@ public class UsernamePasswordAuthenticationProvider implements AuthenticationPro
|
||||
private final UserManager userManager;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final AuthorityManager authorityManager;
|
||||
private final UserCredentialRepository userCredentialRepository;
|
||||
|
||||
@Autowired
|
||||
public UsernamePasswordAuthenticationProvider(
|
||||
UserManager userManager,
|
||||
PasswordEncoder passwordEncoder,
|
||||
AuthorityManager authorityManager
|
||||
AuthorityManager authorityManager,
|
||||
UserCredentialRepository userCredentialRepository
|
||||
) {
|
||||
this.userManager = userManager;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.authorityManager = authorityManager;
|
||||
this.userCredentialRepository = userCredentialRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -49,14 +56,20 @@ public class UsernamePasswordAuthenticationProvider implements AuthenticationPro
|
||||
throw new BizException(HttpStatus.UNAUTHORIZED, "用户名或密码错误。");
|
||||
}
|
||||
|
||||
// get user credentials from database
|
||||
var userCredentials = userCredentialRepository.findOne(Example.of(UserCredential.builder()
|
||||
.provider(CredentialProvider.LOCAL)
|
||||
.userId(user.getId())
|
||||
.build()))
|
||||
.orElseThrow(() -> new BizException(HttpStatus.UNAUTHORIZED, "您还没有配置密码,请使用第三方账号登录"));
|
||||
|
||||
// validate password
|
||||
if (!passwordEncoder.matches(usernamePasswordAuthentication.getCredentials(), user.getPassword())) {
|
||||
if (!passwordEncoder.matches(usernamePasswordAuthentication.getCredentials(), userCredentials.getCredential())) {
|
||||
log.error("User {} is trying to authenticate but password is incorrect.", usernamePasswordAuthentication.getPrincipal());
|
||||
throw new BizException(HttpStatus.UNAUTHORIZED, "用户名或密码错误。");
|
||||
}
|
||||
|
||||
// erase credentials
|
||||
user.setPassword(null);
|
||||
usernamePasswordAuthentication.eraseCredentials();
|
||||
|
||||
// get authorities
|
||||
|
||||
@@ -3,7 +3,9 @@ package com.onixbyte.helix.service;
|
||||
import com.onixbyte.helix.domain.entity.Setting;
|
||||
import com.onixbyte.helix.domain.entity.User;
|
||||
import com.onixbyte.helix.domain.web.request.LoginRequest;
|
||||
import com.onixbyte.helix.enumeration.ApplicationMode;
|
||||
import com.onixbyte.helix.exception.BizException;
|
||||
import com.onixbyte.helix.manager.ApplicationManager;
|
||||
import com.onixbyte.helix.manager.CaptchaManager;
|
||||
import com.onixbyte.helix.manager.SecurityManager;
|
||||
import com.onixbyte.helix.manager.SettingManager;
|
||||
@@ -30,18 +32,20 @@ public class AuthService {
|
||||
private final AuthenticationManager authenticationManager;
|
||||
private final SettingManager settingManager;
|
||||
private final SecurityManager securityManager;
|
||||
private final ApplicationManager applicationManager;
|
||||
|
||||
@Autowired
|
||||
public AuthService(
|
||||
CaptchaManager captchaManager,
|
||||
AuthenticationManager authenticationManager,
|
||||
SettingManager settingManager,
|
||||
SecurityManager securityManager
|
||||
) {
|
||||
SecurityManager securityManager,
|
||||
ApplicationManager applicationManager) {
|
||||
this.captchaManager = captchaManager;
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.settingManager = settingManager;
|
||||
this.securityManager = securityManager;
|
||||
this.applicationManager = applicationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,10 +98,22 @@ public class AuthService {
|
||||
|
||||
public ResponseCookie buildCookie(String cookieName, String token) {
|
||||
var cookieBuilder = ResponseCookie.from(cookieName, token)
|
||||
.httpOnly(true)
|
||||
.maxAge(securityManager.getTokenValidDuration())
|
||||
.secure(true)
|
||||
.path("/");
|
||||
|
||||
var applicationMode = applicationManager.getApplicationMode();
|
||||
switch (applicationMode) {
|
||||
case PRODUCTION -> {
|
||||
cookieBuilder.httpOnly(true);
|
||||
}
|
||||
case DEVELOPMENT -> {
|
||||
cookieBuilder.sameSite("NONE");
|
||||
}
|
||||
case null, default -> {
|
||||
}
|
||||
}
|
||||
|
||||
return cookieBuilder.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import com.onixbyte.helix.domain.web.request.ResetPasswordRequest;
|
||||
import com.onixbyte.helix.domain.web.request.EditUserRequest;
|
||||
import com.onixbyte.helix.domain.web.response.UserDetailResponse;
|
||||
import com.onixbyte.helix.manager.*;
|
||||
import com.onixbyte.helix.mapper.UserCredentialMapper;
|
||||
import com.onixbyte.helix.repository.UserCredentialRepository;
|
||||
import com.onixbyte.identitygenerator.IdentityGenerator;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -35,6 +37,8 @@ public class UserService {
|
||||
private final ApplicationManager applicationManager;
|
||||
private final DepartmentManager departmentManager;
|
||||
private final PositionManager positionManager;
|
||||
private final UserCredentialRepository userCredentialRepository;
|
||||
private final UserCredentialMapper userCredentialMapper;
|
||||
|
||||
@Autowired
|
||||
public UserService(
|
||||
@@ -45,8 +49,8 @@ public class UserService {
|
||||
PasswordEncoder passwordEncoder,
|
||||
ApplicationManager applicationManager,
|
||||
DepartmentManager departmentManager,
|
||||
PositionManager positionManager
|
||||
) {
|
||||
PositionManager positionManager,
|
||||
UserCredentialRepository userCredentialRepository, UserCredentialMapper userCredentialMapper) {
|
||||
this.userManager = userManager;
|
||||
this.userIdentityGenerator = userIdentityGenerator;
|
||||
this.roleManager = roleManager;
|
||||
@@ -55,6 +59,8 @@ public class UserService {
|
||||
this.applicationManager = applicationManager;
|
||||
this.departmentManager = departmentManager;
|
||||
this.positionManager = positionManager;
|
||||
this.userCredentialRepository = userCredentialRepository;
|
||||
this.userCredentialMapper = userCredentialMapper;
|
||||
}
|
||||
|
||||
public Page<UserDetailResponse> queryUserDetailsPage(Pageable pageable, QueryUserRequest request) {
|
||||
@@ -98,7 +104,6 @@ public class UserService {
|
||||
var user = userManager.save(User.builder()
|
||||
.id(userIdentityGenerator.nextId())
|
||||
.username(request.username())
|
||||
.password(passwordEncoder.encode(request.password()))
|
||||
.fullName(request.fullName())
|
||||
.email(request.email())
|
||||
.regionAbbreviation(request.regionAbbreviation())
|
||||
@@ -168,10 +173,7 @@ public class UserService {
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
public void resetPassword(ResetPasswordRequest request) {
|
||||
userManager.updateUser(User.builder()
|
||||
.id(request.id())
|
||||
.password(request.password())
|
||||
.build());
|
||||
userManager.updateUserPassword(request);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Throwable.class)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!--
|
||||
~ Copyright (c) 2024-2026 OnixByte
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
-->
|
||||
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.onixbyte.helix.mapper.UserCredentialMapper">
|
||||
<update id="updateUserCredential">
|
||||
UPDATE user_credentials
|
||||
SET credential = #{encodedPassword}
|
||||
WHERE user_id = #{userId}
|
||||
AND provider = 'LOCAL'::credential_provider
|
||||
</update>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user