From 0a10c64278a16f2324f563692c73406d3a976f23 Mon Sep 17 00:00:00 2001 From: zihluwang Date: Mon, 5 Jan 2026 23:53:31 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E8=B0=83=E6=95=B4=20Token=20?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #1 --- .../helix/config/AuthenticationConfig.java | 9 +++-- .../helix/controller/AuthController.java | 29 ++++++++++++--- .../helix/manager/ApplicationManager.java | 21 ++++++++++- .../properties/ApplicationProperties.java | 3 +- .../properties/AuthenticationProperties.java | 11 ++++++ .../onixbyte/helix/service/AuthService.java | 36 ++++++++++++------- .../onixbyte/helix/service/TokenService.java | 24 +++++++++++++ .../onixbyte/helix/service/UserService.java | 11 ++++++ .../onixbyte/helix/shared/TokenConstant.java | 2 ++ 9 files changed, 125 insertions(+), 21 deletions(-) create mode 100644 src/main/java/com/onixbyte/helix/properties/AuthenticationProperties.java create mode 100644 src/main/java/com/onixbyte/helix/service/TokenService.java diff --git a/src/main/java/com/onixbyte/helix/config/AuthenticationConfig.java b/src/main/java/com/onixbyte/helix/config/AuthenticationConfig.java index d5aff8a..3fb80d9 100644 --- a/src/main/java/com/onixbyte/helix/config/AuthenticationConfig.java +++ b/src/main/java/com/onixbyte/helix/config/AuthenticationConfig.java @@ -1,6 +1,7 @@ package com.onixbyte.helix.config; import com.onixbyte.helix.properties.ApplicationProperties; +import com.onixbyte.helix.properties.AuthenticationProperties; import com.onixbyte.helix.properties.MsalProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; @@ -18,11 +19,15 @@ import org.springframework.context.annotation.Configuration; * application context. * * @author zihluwang - * @since 1.0.0 * @see MsalProperties * @see EnableConfigurationProperties + * @since 1.0.0 */ @Configuration -@EnableConfigurationProperties({MsalProperties.class, ApplicationProperties.class}) +@EnableConfigurationProperties({ + MsalProperties.class, + AuthenticationProperties.class, + ApplicationProperties.class +}) public class AuthenticationConfig { } diff --git a/src/main/java/com/onixbyte/helix/controller/AuthController.java b/src/main/java/com/onixbyte/helix/controller/AuthController.java index 36d4ed2..4a3153a 100644 --- a/src/main/java/com/onixbyte/helix/controller/AuthController.java +++ b/src/main/java/com/onixbyte/helix/controller/AuthController.java @@ -1,11 +1,17 @@ package com.onixbyte.helix.controller; import com.onixbyte.helix.domain.web.request.LoginRequest; -import com.onixbyte.helix.domain.web.response.LoginSuccessResponse; +import com.onixbyte.helix.domain.web.response.UserDetailResponse; import com.onixbyte.helix.service.AuthService; +import com.onixbyte.helix.service.TokenService; +import com.onixbyte.helix.service.UserService; +import com.onixbyte.helix.shared.TokenConstant; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -15,10 +21,18 @@ public class AuthController { private static final Logger log = LoggerFactory.getLogger(AuthController.class); private final AuthService authService; + private final TokenService tokenService; + private final UserService userService; @Autowired - public AuthController(AuthService authService) { + public AuthController( + AuthService authService, + TokenService tokenService, + UserService userService + ) { this.authService = authService; + this.tokenService = tokenService; + this.userService = userService; } /** @@ -28,10 +42,17 @@ public class AuthController { * @return detailed user info and authentication token */ @PostMapping("/login") - public LoginSuccessResponse loginWithUsernameAndPassword( + public ResponseEntity loginWithUsernameAndPassword( @Validated @RequestBody LoginRequest request ) { - return authService.login(request); + var user = authService.login(request); + var token = tokenService.generateToken(user); + + var cookie = authService.buildCookie(TokenConstant.TOKEN_NAME, token); + + return ResponseEntity.status(HttpStatus.OK) + .header(HttpHeaders.SET_COOKIE, cookie.toString()) + .body(userService.getDetail(user)); } @GetMapping("/register-enabled") diff --git a/src/main/java/com/onixbyte/helix/manager/ApplicationManager.java b/src/main/java/com/onixbyte/helix/manager/ApplicationManager.java index 813cf8d..d1b3a27 100644 --- a/src/main/java/com/onixbyte/helix/manager/ApplicationManager.java +++ b/src/main/java/com/onixbyte/helix/manager/ApplicationManager.java @@ -1,20 +1,39 @@ package com.onixbyte.helix.manager; import com.onixbyte.helix.properties.ApplicationProperties; +import com.onixbyte.helix.properties.AuthenticationProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.util.Optional; + @Component public class ApplicationManager { private final ApplicationProperties applicationProperties; + private final AuthenticationProperties authenticationProperties; @Autowired - public ApplicationManager(ApplicationProperties applicationProperties) { + public ApplicationManager(ApplicationProperties applicationProperties, AuthenticationProperties authenticationProperties) { this.applicationProperties = applicationProperties; + this.authenticationProperties = authenticationProperties; } public String getDefaultEmail() { return applicationProperties.defaultEmail(); } + + public String getExternalHost() { + return applicationProperties.externalHost(); + } + + public boolean isSslEnabled() { + return Optional.ofNullable(authenticationProperties.sslEnabled()) + .orElse(false); + } + + public boolean isSecureCookieEnabled() { + return Optional.ofNullable(authenticationProperties.secureCookieEnabled()) + .orElse(false); + } } diff --git a/src/main/java/com/onixbyte/helix/properties/ApplicationProperties.java b/src/main/java/com/onixbyte/helix/properties/ApplicationProperties.java index 1ce7d27..2a725a4 100644 --- a/src/main/java/com/onixbyte/helix/properties/ApplicationProperties.java +++ b/src/main/java/com/onixbyte/helix/properties/ApplicationProperties.java @@ -5,6 +5,7 @@ import org.springframework.boot.context.properties.bind.DefaultValue; @ConfigurationProperties(prefix = "app.common") public record ApplicationProperties( - @DefaultValue("default@helix.onixbyte.dev") String defaultEmail + @DefaultValue("default@helix.onixbyte.dev") String defaultEmail, + @DefaultValue("helix.onixbyte.dev") String externalHost ) { } diff --git a/src/main/java/com/onixbyte/helix/properties/AuthenticationProperties.java b/src/main/java/com/onixbyte/helix/properties/AuthenticationProperties.java new file mode 100644 index 0000000..fca1673 --- /dev/null +++ b/src/main/java/com/onixbyte/helix/properties/AuthenticationProperties.java @@ -0,0 +1,11 @@ +package com.onixbyte.helix.properties; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.bind.DefaultValue; + +@ConfigurationProperties(prefix = "app.auth") +public record AuthenticationProperties( + @DefaultValue("true") Boolean sslEnabled, + @DefaultValue("true") Boolean secureCookieEnabled +) { +} diff --git a/src/main/java/com/onixbyte/helix/service/AuthService.java b/src/main/java/com/onixbyte/helix/service/AuthService.java index ff972c9..c6ebcc1 100644 --- a/src/main/java/com/onixbyte/helix/service/AuthService.java +++ b/src/main/java/com/onixbyte/helix/service/AuthService.java @@ -1,18 +1,17 @@ package com.onixbyte.helix.service; -import com.onixbyte.helix.client.TokenClient; -import com.onixbyte.helix.shared.SettingName; 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.domain.web.response.LoginSuccessResponse; import com.onixbyte.helix.exception.BizException; -import com.onixbyte.helix.manager.CaptchaManager; -import com.onixbyte.helix.manager.SettingManager; +import com.onixbyte.helix.manager.*; import com.onixbyte.helix.security.authentication.UsernamePasswordAuthentication; +import com.onixbyte.helix.shared.SettingName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.stereotype.Service; @@ -26,20 +25,20 @@ public class AuthService { private final CaptchaManager captchaManager; private final AuthenticationManager authenticationManager; - private final TokenClient tokenClient; private final SettingManager settingManager; + private final ApplicationManager applicationManager; @Autowired public AuthService( CaptchaManager captchaManager, AuthenticationManager authenticationManager, - TokenClient tokenClient, - SettingManager settingManager + SettingManager settingManager, + ApplicationManager applicationManager ) { this.captchaManager = captchaManager; this.authenticationManager = authenticationManager; - this.tokenClient = tokenClient; this.settingManager = settingManager; + this.applicationManager = applicationManager; } /** @@ -49,7 +48,7 @@ public class AuthService { * @return user information and user identity token * @throws BizException if the user does not exist, or the username and password are incorrect */ - public LoginSuccessResponse login(LoginRequest request) { + public User login(LoginRequest request) { var captchaEnabled = Optional.ofNullable(settingManager.getSettingByName(SettingName.CAPTCHA_ENABLED)) .map(Setting::asBoolean) .orElse(false); @@ -76,9 +75,7 @@ public class AuthService { "Cannot perform login due to server crashes."); } - var token = tokenClient.generateToken(authentication.getDetails()); - - return new LoginSuccessResponse(token, authentication.getDetails()); + return authentication.getDetails(); } /** @@ -91,4 +88,17 @@ public class AuthService { .map(Setting::asBoolean) .orElse(false); } + + public ResponseCookie buildCookie(String cookieName, String token) { + var cookieBuilder = ResponseCookie.from(cookieName, token) + .httpOnly(true) + .secure(applicationManager.isSslEnabled()) + .path("/"); + + if (applicationManager.isSecureCookieEnabled()) { + cookieBuilder.domain(applicationManager.getExternalHost()); + } + + return cookieBuilder.build(); + } } diff --git a/src/main/java/com/onixbyte/helix/service/TokenService.java b/src/main/java/com/onixbyte/helix/service/TokenService.java new file mode 100644 index 0000000..5c5a214 --- /dev/null +++ b/src/main/java/com/onixbyte/helix/service/TokenService.java @@ -0,0 +1,24 @@ +package com.onixbyte.helix.service; + +import com.auth0.jwt.interfaces.DecodedJWT; +import com.onixbyte.helix.client.TokenClient; +import com.onixbyte.helix.domain.entity.User; +import org.springframework.stereotype.Service; + +@Service +public class TokenService { + + private final TokenClient tokenClient; + + public TokenService(TokenClient tokenClient) { + this.tokenClient = tokenClient; + } + + public String generateToken(User user) { + return tokenClient.generateToken(user); + } + + public DecodedJWT verifyToken(String token) { + return tokenClient.verifyToken(token); + } +} diff --git a/src/main/java/com/onixbyte/helix/service/UserService.java b/src/main/java/com/onixbyte/helix/service/UserService.java index b9f13a6..361aa7d 100644 --- a/src/main/java/com/onixbyte/helix/service/UserService.java +++ b/src/main/java/com/onixbyte/helix/service/UserService.java @@ -179,4 +179,15 @@ public class UserService { userRoleManager.deleteByUserId(userId); userManager.deleteById(userId); } + + public UserDetailResponse getDetail(User user) { + var department = departmentManager.selectById(user.getDepartmentId()); + var position = positionManager.selectById(user.getPositionId()); + + return UserDetailResponse.builder() + .user(user) + .departmentName(department.getName()) + .positionName(position.getName()) + .build(); + } } diff --git a/src/main/java/com/onixbyte/helix/shared/TokenConstant.java b/src/main/java/com/onixbyte/helix/shared/TokenConstant.java index 739ad25..c4e8d59 100644 --- a/src/main/java/com/onixbyte/helix/shared/TokenConstant.java +++ b/src/main/java/com/onixbyte/helix/shared/TokenConstant.java @@ -4,6 +4,8 @@ public final class TokenConstant { public static final String TOKEN_HEADER_NAME = "Authorization"; + public static final String TOKEN_NAME = "AccessToken"; + public static final String TOKEN_PREFIX = "Bearer "; public static final int TOKEN_PREFIX_LENGTH = TOKEN_PREFIX.length();