diff --git a/src/main/java/com/onixbyte/deltaforceguide/repository/BaseRepository.java b/src/main/java/com/onixbyte/deltaforceguide/repository/BaseRepository.java new file mode 100644 index 0000000..93ab920 --- /dev/null +++ b/src/main/java/com/onixbyte/deltaforceguide/repository/BaseRepository.java @@ -0,0 +1,7 @@ +package com.onixbyte.deltaforceguide.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface BaseRepository extends JpaRepository, JpaSpecificationExecutor { +} diff --git a/src/main/java/com/onixbyte/deltaforceguide/repository/FirearmRepository.java b/src/main/java/com/onixbyte/deltaforceguide/repository/FirearmRepository.java index 46c84b9..881ee97 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/repository/FirearmRepository.java +++ b/src/main/java/com/onixbyte/deltaforceguide/repository/FirearmRepository.java @@ -15,7 +15,7 @@ import java.util.List; * @author zihluwang */ @Repository -public interface FirearmRepository extends JpaRepository { +public interface FirearmRepository extends BaseRepository { Page findAllByType(FirearmType type, Pageable pageable); diff --git a/src/main/java/com/onixbyte/deltaforceguide/repository/ModificationRepository.java b/src/main/java/com/onixbyte/deltaforceguide/repository/ModificationRepository.java index 3d9fcc0..bd16813 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/repository/ModificationRepository.java +++ b/src/main/java/com/onixbyte/deltaforceguide/repository/ModificationRepository.java @@ -20,7 +20,7 @@ import java.util.Optional; * @author zihluwang */ @Repository -public interface ModificationRepository extends JpaRepository { +public interface ModificationRepository extends BaseRepository { @EntityGraph(attributePaths = {"firearm"}) Page findAllBy(Pageable pageable); diff --git a/src/main/java/com/onixbyte/deltaforceguide/repository/UserCredentialRepository.java b/src/main/java/com/onixbyte/deltaforceguide/repository/UserCredentialRepository.java index 53776e3..36c2181 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/repository/UserCredentialRepository.java +++ b/src/main/java/com/onixbyte/deltaforceguide/repository/UserCredentialRepository.java @@ -2,10 +2,7 @@ package com.onixbyte.deltaforceguide.repository; import com.onixbyte.deltaforceguide.domain.entity.UserCredential; import com.onixbyte.deltaforceguide.domain.entity.UserCredentialId; -import org.springframework.data.jpa.repository.Modifying; -import org.springframework.data.jpa.repository.EntityGraph; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; +import org.springframework.data.jpa.repository.*; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -18,7 +15,9 @@ import java.util.Optional; * @author zihluwang */ @Repository -public interface UserCredentialRepository extends JpaRepository { +public interface UserCredentialRepository extends + JpaRepository, + JpaSpecificationExecutor { /** * Find all credentials belonging to a given user. diff --git a/src/main/java/com/onixbyte/deltaforceguide/repository/UserRepository.java b/src/main/java/com/onixbyte/deltaforceguide/repository/UserRepository.java index 7a0a363..cf81ca8 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/repository/UserRepository.java +++ b/src/main/java/com/onixbyte/deltaforceguide/repository/UserRepository.java @@ -16,7 +16,7 @@ import java.util.Optional; * @author zihluwang */ @Repository -public interface UserRepository extends JpaRepository { +public interface UserRepository extends BaseRepository { @Override @EntityGraph(attributePaths = {"credentials"}) diff --git a/src/main/java/com/onixbyte/deltaforceguide/repository/UserRoleRepository.java b/src/main/java/com/onixbyte/deltaforceguide/repository/UserRoleRepository.java index f2ce1e8..a227b72 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/repository/UserRoleRepository.java +++ b/src/main/java/com/onixbyte/deltaforceguide/repository/UserRoleRepository.java @@ -17,7 +17,7 @@ import java.util.List; * @author zihluwang */ @Repository -public interface UserRoleRepository extends JpaRepository { +public interface UserRoleRepository extends BaseRepository { /** * Find all roles assigned to a given user. diff --git a/src/main/java/com/onixbyte/deltaforceguide/security/annotation/RequiresAdmin.java b/src/main/java/com/onixbyte/deltaforceguide/security/annotation/RequiresAdmin.java new file mode 100644 index 0000000..83ebd28 --- /dev/null +++ b/src/main/java/com/onixbyte/deltaforceguide/security/annotation/RequiresAdmin.java @@ -0,0 +1,14 @@ +package com.onixbyte.deltaforceguide.security.annotation; + +import org.springframework.security.access.prepost.PreAuthorize; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@PreAuthorize("isAuthenticated() && hasAnyRole('ROLE_ADMIN', 'ROLE_SUPER_USER')") +public @interface RequiresAdmin { +} diff --git a/src/main/java/com/onixbyte/deltaforceguide/security/annotation/RequiresSuperUser.java b/src/main/java/com/onixbyte/deltaforceguide/security/annotation/RequiresSuperUser.java new file mode 100644 index 0000000..1be1899 --- /dev/null +++ b/src/main/java/com/onixbyte/deltaforceguide/security/annotation/RequiresSuperUser.java @@ -0,0 +1,14 @@ +package com.onixbyte.deltaforceguide.security.annotation; + +import org.springframework.security.access.prepost.PreAuthorize; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ElementType.METHOD, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@PreAuthorize("isAuthenticated() && hasRole('ROLE_SUPER_USER')") +public @interface RequiresSuperUser { +} diff --git a/src/main/java/com/onixbyte/deltaforceguide/security/authentication/UsernamePasswordAuthentication.java b/src/main/java/com/onixbyte/deltaforceguide/security/authentication/UsernamePasswordAuthentication.java index c6eb826..cf1bc84 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/security/authentication/UsernamePasswordAuthentication.java +++ b/src/main/java/com/onixbyte/deltaforceguide/security/authentication/UsernamePasswordAuthentication.java @@ -93,5 +93,9 @@ public class UsernamePasswordAuthentication implements Authentication, Credentia public void setDetails(User user) { this.user = user; } + + public void setAuthorities(Collection authorities) { + this.authorities = new ArrayList<>(authorities); + } } diff --git a/src/main/java/com/onixbyte/deltaforceguide/security/provider/UsernamePasswordAuthenticationProvider.java b/src/main/java/com/onixbyte/deltaforceguide/security/provider/UsernamePasswordAuthenticationProvider.java index 8fc1614..af29ba1 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/security/provider/UsernamePasswordAuthenticationProvider.java +++ b/src/main/java/com/onixbyte/deltaforceguide/security/provider/UsernamePasswordAuthenticationProvider.java @@ -1,22 +1,28 @@ package com.onixbyte.deltaforceguide.security.provider; -import com.onixbyte.deltaforceguide.domain.entity.UserCredential; +import com.onixbyte.deltaforceguide.domain.entity.UserRole; import com.onixbyte.deltaforceguide.exeption.InternalServerErrorException; import com.onixbyte.deltaforceguide.exeption.UnauthorisedException; import com.onixbyte.deltaforceguide.manager.UserManager; import com.onixbyte.deltaforceguide.repository.UserCredentialRepository; +import com.onixbyte.deltaforceguide.repository.UserRoleRepository; import com.onixbyte.deltaforceguide.security.authentication.UsernamePasswordAuthentication; import com.onixbyte.deltaforceguide.shared.CredentialProvider; +import com.onixbyte.deltaforceguide.shared.Role; +import jakarta.persistence.criteria.Predicate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Example; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.List; + /** * Authentication provider that validates username/password credentials against stored BCrypt hashes. * @@ -29,16 +35,18 @@ public class UsernamePasswordAuthenticationProvider implements AuthenticationPro private final UserManager userManager; private final PasswordEncoder passwordEncoder; private final UserCredentialRepository userCredentialRepository; + private final UserRoleRepository userRoleRepository; @Autowired public UsernamePasswordAuthenticationProvider( UserManager userManager, PasswordEncoder passwordEncoder, - UserCredentialRepository userCredentialRepository - ) { + UserCredentialRepository userCredentialRepository, + UserRoleRepository userRoleRepository) { this.userManager = userManager; this.passwordEncoder = passwordEncoder; this.userCredentialRepository = userCredentialRepository; + this.userRoleRepository = userRoleRepository; } /** @@ -62,12 +70,13 @@ public class UsernamePasswordAuthenticationProvider implements AuthenticationPro var user = userContainer.get(); - var userCredentialExample = new UserCredential(); - userCredentialExample.setUserId(user.getId()); - userCredentialExample.setProvider(CredentialProvider.LOCAL); - // get userContainer credentials from database - var userCredentials = userCredentialRepository.findOne(Example.of(userCredentialExample)) + var userCredentials = userCredentialRepository.findOne((root, query, cb) -> { + var predicates = new ArrayList(); + predicates.add(cb.equal(root.get("provider"), CredentialProvider.LOCAL)); + predicates.add(cb.equal(root.get("userId"), user.getId())); + return cb.and(predicates.toArray(Predicate[]::new)); + }) .orElseThrow(() -> new UnauthorisedException("您还没有配置密码,请联系管理员处理。")); // validate password @@ -76,12 +85,21 @@ public class UsernamePasswordAuthenticationProvider implements AuthenticationPro throw new UnauthorisedException("用户名或密码错误。"); } + var role = userRoleRepository.findOne((root, query, cb) -> cb.equal(root.get("userId"), user.getId())) + .orElseGet(() -> { + var ur = new UserRole(); + ur.setUserId(user.getId()); + ur.setRole(Role.ROLE_USER); + return ur; + }); + // erase credentials usernamePasswordAuthentication.eraseCredentials(); // set values usernamePasswordAuthentication.setAuthenticated(true); usernamePasswordAuthentication.setDetails(user); + usernamePasswordAuthentication.setAuthorities(List.of(new SimpleGrantedAuthority(role.getRole()))); return usernamePasswordAuthentication; } diff --git a/src/main/java/com/onixbyte/deltaforceguide/shared/Role.java b/src/main/java/com/onixbyte/deltaforceguide/shared/Role.java index 719321e..3bea7df 100644 --- a/src/main/java/com/onixbyte/deltaforceguide/shared/Role.java +++ b/src/main/java/com/onixbyte/deltaforceguide/shared/Role.java @@ -7,6 +7,10 @@ package com.onixbyte.deltaforceguide.shared; */ public class Role { + public static final String ROLE_USER_VALUE = "USER"; + public static final String ROLE_ADMIN_VALUE = "ADMIN"; + public static final String ROLE_SUPER_USER_VALUE = "SUPER_USER"; + public static final String ROLE_USER = "ROLE_USER"; public static final String ROLE_ADMIN = "ROLE_ADMIN"; public static final String ROLE_SUPER_USER = "ROLE_SUPER_USER";