feat(interactive): fix scaffold startup and add auth lab page

This commit is contained in:
likingcode
2026-03-09 23:57:49 +08:00
parent f846616e0b
commit 3330cd6e8d
7 changed files with 362 additions and 2 deletions

View File

@@ -27,7 +27,7 @@ public class PerformanceAspect {
public final AtomicLong errorCount = new AtomicLong();
}
@Around("execution(* com.example.scaffold..*.*(..))")
@Around("execution(* com.example.scaffold..*.*(..)) && !within(com.example.scaffold.security..*) && !within(org.springframework.web.filter..*)")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
String key = joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature().getName();
long startTime = System.nanoTime();

View File

@@ -0,0 +1,180 @@
package com.example.scaffold.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.example.scaffold.security.jwt.JwtUtil;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 认证控制器 - 支持 JWT 和 Sa-Token
*
* 学习要点:
* 1. 登录认证流程
* 2. Token 生成与刷新
* 3. 登出处理
*/
@Slf4j
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final JwtUtil jwtUtil;
private final AuthenticationManager authenticationManager;
public AuthController(JwtUtil jwtUtil, ObjectProvider<AuthenticationManager> authenticationManagerProvider) {
this.jwtUtil = jwtUtil;
this.authenticationManager = authenticationManagerProvider.getIfAvailable();
}
@Value("${auth.type:none}")
private String authType;
/**
* 登录 - 支持多种认证方式
*/
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest request) {
log.info("🔐 用户登录: username={}, authType={}", request.getUsername(), authType);
return switch (authType) {
case "jwt" -> jwtLogin(request);
case "satoken" -> saTokenLogin(request);
default -> demoLogin(request);
};
}
/**
* JWT 登录
*/
private ResponseEntity<?> jwtLogin(LoginRequest request) {
if (authenticationManager == null) {
return ResponseEntity.status(503).body(Map.of(
"success", false,
"message", "当前 profile 未启用 JWT AuthenticationManager请切到 advanced 或 auth.type=jwt"
));
}
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
request.getUsername(),
request.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
// 生成 JWT Token
String token = jwtUtil.generateToken(1L, request.getUsername(), "USER");
return ResponseEntity.ok(Map.of(
"success", true,
"token", token,
"type", "Bearer",
"username", request.getUsername()
));
} catch (Exception e) {
return ResponseEntity.status(401).body(Map.of(
"success", false,
"message", "用户名或密码错误"
));
}
}
/**
* Sa-Token 登录
*/
private ResponseEntity<?> saTokenLogin(LoginRequest request) {
// 简单校验(实际应从数据库校验)
if ("admin".equals(request.getUsername()) && "admin123".equals(request.getPassword())) {
StpUtil.login(10001);
return ResponseEntity.ok(Map.of(
"success", true,
"token", StpUtil.getTokenValue(),
"type", "satoken",
"username", request.getUsername()
));
}
return ResponseEntity.status(401).body(Map.of(
"success", false,
"message", "用户名或密码错误"
));
}
/**
* 演示登录(无认证)
*/
private ResponseEntity<?> demoLogin(LoginRequest request) {
return ResponseEntity.ok(Map.of(
"success", true,
"message", "演示模式,无需认证",
"username", request.getUsername()
));
}
/**
* 登出
*/
@PostMapping("/logout")
public ResponseEntity<?> logout() {
if ("satoken".equals(authType)) {
StpUtil.logout();
}
SecurityContextHolder.clearContext();
return ResponseEntity.ok(Map.of("success", true, "message", "登出成功"));
}
/**
* 获取当前登录用户信息
*/
@GetMapping("/info")
public ResponseEntity<?> getCurrentUser() {
if ("satoken".equals(authType) && StpUtil.isLogin()) {
return ResponseEntity.ok(Map.of(
"loggedIn", true,
"userId", StpUtil.getLoginId(),
"authType", authType
));
}
return ResponseEntity.ok(Map.of(
"loggedIn", false,
"authType", authType
));
}
/**
* 刷新 TokenJWT
*/
@PostMapping("/refresh")
public ResponseEntity<?> refreshToken(@RequestHeader("Authorization") String authHeader) {
if (!authHeader.startsWith("Bearer ")) {
return ResponseEntity.badRequest().body(Map.of("error", "无效的 Token 格式"));
}
String token = authHeader.substring(7);
if (!jwtUtil.validateToken(token)) {
return ResponseEntity.status(401).body(Map.of("error", "Token 无效或已过期"));
}
String newToken = jwtUtil.refreshToken(token);
return ResponseEntity.ok(Map.of("token", newToken));
}
@Data
public static class LoginRequest {
private String username;
private String password;
}
}

View File

@@ -0,0 +1,24 @@
package com.example.scaffold.security;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@ConditionalOnProperty(name = "auth.type", havingValue = "none", matchIfMissing = true)
public class LearningPermitAllSecurityConfig {
@Bean
public SecurityFilterChain permitAllSecurityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth.anyRequest().permitAll())
.headers(headers -> headers.frameOptions(frame -> frame.sameOrigin()));
return http.build();
}
}