feat(interactive): fix scaffold startup and add auth lab page
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新 Token(JWT)
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user