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