feat: add authentication and tenant context
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
package com.linhelp.common.config;
|
||||
|
||||
import com.linhelp.common.security.RoleGuardInterceptor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class SaTokenConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(new RoleGuardInterceptor())
|
||||
.addPathPatterns("/api/**")
|
||||
.excludePathPatterns("/api/auth/**");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.linhelp.common.security;
|
||||
|
||||
import com.linhelp.common.api.ApiResponse;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
@Validated
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController {
|
||||
private final AuthService authService;
|
||||
|
||||
public AuthController(AuthService authService) {
|
||||
this.authService = authService;
|
||||
}
|
||||
|
||||
@PostMapping("/admin-login")
|
||||
public ApiResponse<LoginResponse> adminLogin(@Valid @RequestBody LoginRequest request) {
|
||||
return ApiResponse.ok(authService.adminLogin(request));
|
||||
}
|
||||
|
||||
@PostMapping("/dev-miniapp-login")
|
||||
public ApiResponse<LoginResponse> devMiniappLogin(@Valid @RequestBody LoginRequest request) {
|
||||
return ApiResponse.ok(authService.devMiniappLogin(request));
|
||||
}
|
||||
|
||||
@PostMapping("/logout")
|
||||
public ApiResponse<Void> logout() {
|
||||
authService.logout();
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
|
||||
@GetMapping("/me")
|
||||
public ApiResponse<CurrentUser> me() {
|
||||
return ApiResponse.ok(authService.currentUser());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.linhelp.common.security;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.linhelp.common.exception.BizException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class AuthService {
|
||||
private static final Long DEFAULT_TENANT_ID = 1L;
|
||||
private static final Long DEFAULT_COMMUNITY_ID = 1L;
|
||||
private final Map<String, DemoAccount> demoAccounts = new HashMap<String, DemoAccount>();
|
||||
|
||||
public AuthService() {
|
||||
demoAccounts.put("admin", new DemoAccount(1L, "admin123", "COMMUNITY_ADMIN"));
|
||||
demoAccounts.put("merchant", new DemoAccount(2L, "merchant123", "MERCHANT"));
|
||||
demoAccounts.put("rider", new DemoAccount(3L, "rider123", "RIDER"));
|
||||
}
|
||||
|
||||
public LoginResponse adminLogin(LoginRequest request) {
|
||||
DemoAccount account = demoAccounts.get(request.getUsername());
|
||||
if (account == null || !account.password.equals(request.getPassword())) {
|
||||
throw new BizException(401, "账号或密码错误");
|
||||
}
|
||||
return login(account.userId, account.roleCode);
|
||||
}
|
||||
|
||||
public LoginResponse devMiniappLogin(LoginRequest request) {
|
||||
return login(1000L, "RESIDENT");
|
||||
}
|
||||
|
||||
public CurrentUser currentUser() {
|
||||
StpUtil.checkLogin();
|
||||
return new CurrentUser(
|
||||
StpUtil.getLoginIdAsLong(),
|
||||
(Long) StpUtil.getSession().get("tenantId"),
|
||||
(Long) StpUtil.getSession().get("communityId"),
|
||||
(String) StpUtil.getSession().get("roleCode")
|
||||
);
|
||||
}
|
||||
|
||||
public void logout() {
|
||||
StpUtil.logout();
|
||||
}
|
||||
|
||||
private LoginResponse login(Long userId, String roleCode) {
|
||||
StpUtil.login(userId);
|
||||
StpUtil.getSession().set("tenantId", DEFAULT_TENANT_ID);
|
||||
StpUtil.getSession().set("communityId", DEFAULT_COMMUNITY_ID);
|
||||
StpUtil.getSession().set("roleCode", roleCode);
|
||||
return new LoginResponse(StpUtil.getTokenValue(), userId, DEFAULT_TENANT_ID, DEFAULT_COMMUNITY_ID, roleCode);
|
||||
}
|
||||
|
||||
private static class DemoAccount {
|
||||
private final Long userId;
|
||||
private final String password;
|
||||
private final String roleCode;
|
||||
|
||||
private DemoAccount(Long userId, String password, String roleCode) {
|
||||
this.userId = userId;
|
||||
this.password = password;
|
||||
this.roleCode = roleCode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.linhelp.common.security;
|
||||
|
||||
public class CurrentUser {
|
||||
private Long userId;
|
||||
private Long tenantId;
|
||||
private Long communityId;
|
||||
private String roleCode;
|
||||
|
||||
public CurrentUser() {
|
||||
}
|
||||
|
||||
public CurrentUser(Long userId, Long tenantId, Long communityId, String roleCode) {
|
||||
this.userId = userId;
|
||||
this.tenantId = tenantId;
|
||||
this.communityId = communityId;
|
||||
this.roleCode = roleCode;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public Long getCommunityId() {
|
||||
return communityId;
|
||||
}
|
||||
|
||||
public String getRoleCode() {
|
||||
return roleCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.linhelp.common.security;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
public class LoginRequest {
|
||||
@NotBlank
|
||||
private String username;
|
||||
|
||||
@NotBlank
|
||||
private String password;
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.linhelp.common.security;
|
||||
|
||||
public class LoginResponse {
|
||||
private String token;
|
||||
private Long userId;
|
||||
private Long tenantId;
|
||||
private Long communityId;
|
||||
private String roleCode;
|
||||
|
||||
public LoginResponse() {
|
||||
}
|
||||
|
||||
public LoginResponse(String token, Long userId, Long tenantId, Long communityId, String roleCode) {
|
||||
this.token = token;
|
||||
this.userId = userId;
|
||||
this.tenantId = tenantId;
|
||||
this.communityId = communityId;
|
||||
this.roleCode = roleCode;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public Long getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public Long getTenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
public Long getCommunityId() {
|
||||
return communityId;
|
||||
}
|
||||
|
||||
public String getRoleCode() {
|
||||
return roleCode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.linhelp.common.security;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface RequireRole {
|
||||
String[] value();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.linhelp.common.security;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import com.linhelp.common.exception.BizException;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class RoleGuardInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
if (!(handler instanceof HandlerMethod)) {
|
||||
return true;
|
||||
}
|
||||
HandlerMethod method = (HandlerMethod) handler;
|
||||
RequireRole requireRole = method.getMethodAnnotation(RequireRole.class);
|
||||
if (requireRole == null) {
|
||||
requireRole = method.getBeanType().getAnnotation(RequireRole.class);
|
||||
}
|
||||
if (requireRole == null) {
|
||||
return true;
|
||||
}
|
||||
StpUtil.checkLogin();
|
||||
String currentRole = (String) StpUtil.getSession().get("roleCode");
|
||||
for (String role : requireRole.value()) {
|
||||
if (role.equals(currentRole)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
throw new BizException(403, "无权访问");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.linhelp.common.security;
|
||||
|
||||
public final class TenantContext {
|
||||
private static final ThreadLocal<Scope> SCOPE = new ThreadLocal<Scope>();
|
||||
|
||||
private TenantContext() {
|
||||
}
|
||||
|
||||
public static void set(Long tenantId, Long communityId) {
|
||||
SCOPE.set(new Scope(tenantId, communityId));
|
||||
}
|
||||
|
||||
public static Long tenantId() {
|
||||
Scope scope = SCOPE.get();
|
||||
return scope == null ? null : scope.tenantId();
|
||||
}
|
||||
|
||||
public static Long communityId() {
|
||||
Scope scope = SCOPE.get();
|
||||
return scope == null ? null : scope.communityId();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
SCOPE.remove();
|
||||
}
|
||||
|
||||
private static class Scope {
|
||||
private final Long tenantId;
|
||||
private final Long communityId;
|
||||
|
||||
private Scope(Long tenantId, Long communityId) {
|
||||
this.tenantId = tenantId;
|
||||
this.communityId = communityId;
|
||||
}
|
||||
|
||||
private Long tenantId() {
|
||||
return tenantId;
|
||||
}
|
||||
|
||||
private Long communityId() {
|
||||
return communityId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.linhelp.common.security;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class TenantContextTests {
|
||||
|
||||
@AfterEach
|
||||
void clear() {
|
||||
TenantContext.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
void storesTenantAndCommunityForCurrentRequestThread() {
|
||||
TenantContext.set(1L, 10L);
|
||||
|
||||
assertThat(TenantContext.tenantId()).isEqualTo(1L);
|
||||
assertThat(TenantContext.communityId()).isEqualTo(10L);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user