diff --git a/pom.xml b/pom.xml
index b7eda40..4f21cef 100644
--- a/pom.xml
+++ b/pom.xml
@@ -58,11 +58,6 @@
org.springframework.boot
spring-boot-starter-thymeleaf
-
-
- org.springframework.boot
- spring-boot-starter-oauth2-client
-
org.springframework.boot
diff --git a/src/main/java/com/chinaweal/youfool/course/config/SaTokenConfig.java b/src/main/java/com/chinaweal/youfool/course/config/SaTokenConfig.java
new file mode 100644
index 0000000..faf76f9
--- /dev/null
+++ b/src/main/java/com/chinaweal/youfool/course/config/SaTokenConfig.java
@@ -0,0 +1,34 @@
+package com.chinaweal.youfool.course.config;
+
+import cn.dev33.satoken.interceptor.SaInterceptor;
+import cn.dev33.satoken.router.SaRouter;
+import cn.dev33.satoken.stp.StpUtil;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * Sa-Token配置
+ *
+ * @author lroyia
+ * @since 2025/10/24
+ **/
+@Configuration
+public class SaTokenConfig implements WebMvcConfigurer {
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ // 注册Sa-Token拦截器
+ registry.addInterceptor(new SaInterceptor())
+ .addPathPatterns("/**")
+ .excludePathPatterns(
+ "/login",
+ "/error",
+ "/webjars/**",
+ "/css/**",
+ "/js/**",
+ "/user/auth/**",
+ "/oauth2/**"
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/chinaweal/youfool/course/config/WebSecurityConfig.java b/src/main/java/com/chinaweal/youfool/course/config/WebSecurityConfig.java
deleted file mode 100644
index b340e70..0000000
--- a/src/main/java/com/chinaweal/youfool/course/config/WebSecurityConfig.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.chinaweal.youfool.course.config;
-
-import com.chinaweal.youfool.course.security.OAuth2LoginFailureHandler;
-import com.chinaweal.youfool.course.security.OAuth2LoginSuccessHandler;
-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.configuration.EnableWebSecurity;
-import org.springframework.security.web.SecurityFilterChain;
-
-/**
- * Web安全配置
- *
- * @author lroyia
- * @since 2025/10/24
- **/
-@Configuration
-@EnableWebSecurity
-public class WebSecurityConfig {
-
- private final OAuth2LoginSuccessHandler oauth2LoginSuccessHandler;
- private final OAuth2LoginFailureHandler oauth2LoginFailureHandler;
-
- public WebSecurityConfig(OAuth2LoginSuccessHandler oauth2LoginSuccessHandler,
- OAuth2LoginFailureHandler oauth2LoginFailureHandler) {
- this.oauth2LoginSuccessHandler = oauth2LoginSuccessHandler;
- this.oauth2LoginFailureHandler = oauth2LoginFailureHandler;
- }
-
- @Bean
- public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
- http
- .authorizeHttpRequests(authorize -> authorize
- .requestMatchers("/login", "/error", "/webjars/**", "/css/**", "/js/**").permitAll()
- .requestMatchers("/user/auth/**").permitAll()
- .anyRequest().authenticated()
- )
- .oauth2Login(oauth2 -> oauth2
- .loginPage("/login")
- .successHandler(oauth2LoginSuccessHandler)
- .failureHandler(oauth2LoginFailureHandler)
- )
- .csrf(csrf -> csrf.disable())
- .formLogin(form -> form
- .loginPage("/login")
- .permitAll()
- );
-
- return http.build();
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/chinaweal/youfool/course/controller/OAuth2Controller.java b/src/main/java/com/chinaweal/youfool/course/controller/OAuth2Controller.java
new file mode 100644
index 0000000..c763dec
--- /dev/null
+++ b/src/main/java/com/chinaweal/youfool/course/controller/OAuth2Controller.java
@@ -0,0 +1,182 @@
+package com.chinaweal.youfool.course.controller;
+
+import cn.dev33.satoken.session.SaSession;
+import cn.dev33.satoken.stp.StpUtil;
+import com.chinaweal.youfool.framework.springboot.user.entity.UserBase;
+import com.chinaweal.youfool.course.common.constants.SessionConstants;
+import com.chinaweal.youfool.course.entity.SysUser;
+import com.chinaweal.youfool.course.service.SysUserService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.servlet.view.RedirectView;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * OAuth2控制器
+ *
+ * @author lroyia
+ * @since 2025/10/24
+ **/
+@Slf4j
+@Controller
+@RequestMapping("/oauth2")
+public class OAuth2Controller {
+
+ @Value("${gitea.client-id}")
+ private String clientId;
+
+ @Value("${gitea.client-secret}")
+ private String clientSecret;
+
+ @Value("${gitea.auth-url}")
+ private String authUrl;
+
+ @Value("${gitea.token-url}")
+ private String tokenUrl;
+
+ @Value("${gitea.user-url}")
+ private String userUrl;
+
+ @Value("${gitea.redirect-uri}")
+ private String redirectUri;
+
+ @Autowired
+ private SysUserService sysUserService;
+
+ /**
+ * 跳转到Gitea授权页面
+ *
+ * @return 重定向到Gitea授权页面
+ */
+ @GetMapping("/gitea/authorize")
+ public RedirectView authorize() {
+ String state = String.valueOf(System.currentTimeMillis());
+ String url = String.format("%s?client_id=%s&redirect_uri=%s&response_type=code&state=%s&scope=read:user",
+ authUrl, clientId, redirectUri, state);
+ return new RedirectView(url);
+ }
+
+ /**
+ * Gitea回调处理
+ *
+ * @param code 授权码
+ * @param state 状态
+ * @return 重定向到首页
+ */
+ @GetMapping("/gitea/callback")
+ public RedirectView callback(@RequestParam String code, @RequestParam String state) {
+ try {
+ // 获取访问令牌
+ Map tokenParams = new HashMap<>();
+ tokenParams.put("client_id", clientId);
+ tokenParams.put("client_secret", clientSecret);
+ tokenParams.put("code", code);
+ tokenParams.put("grant_type", "authorization_code");
+ tokenParams.put("redirect_uri", redirectUri);
+
+ RestTemplate restTemplate = new RestTemplate();
+ Map tokenResponse = restTemplate.postForObject(tokenUrl, tokenParams, Map.class);
+
+ if (tokenResponse == null || !tokenResponse.containsKey("access_token")) {
+ throw new RuntimeException("获取access_token失败");
+ }
+
+ String accessToken = (String) tokenResponse.get("access_token");
+
+ // 获取用户信息
+ Map userInfo = getUserInfo(accessToken);
+
+ // 处理用户登录
+ handleUserLogin(userInfo);
+
+ return new RedirectView("/course/");
+
+ } catch (Exception e) {
+ log.error("Gitea OAuth2回调处理失败", e);
+ return new RedirectView("/course/login?error=true&message=" + e.getMessage());
+ }
+ }
+
+ /**
+ * 获取用户信息
+ *
+ * @param accessToken 访问令牌
+ * @return 用户信息
+ */
+ private Map getUserInfo(String accessToken) {
+ RestTemplate restTemplate = new RestTemplate();
+ Map headers = new HashMap<>();
+ headers.put("Authorization", "token " + accessToken);
+
+ org.springframework.http.HttpEntity> entity = new org.springframework.http.HttpEntity<>(headers);
+ Map userInfo = restTemplate.exchange(userUrl,
+ org.springframework.http.HttpMethod.GET, entity, Map.class).getBody();
+
+ return userInfo;
+ }
+
+ /**
+ * 处理用户登录
+ *
+ * @param userInfo 用户信息
+ */
+ private void handleUserLogin(Map userInfo) {
+ String username = (String) userInfo.get("username");
+ String email = (String) userInfo.get("email");
+ String avatarUrl = (String) userInfo.get("avatar_url");
+ Integer id = (Integer) userInfo.get("id");
+
+ log.info("Gitea用户登录成功: username={}, email={}, id={}", username, email, id);
+
+ // 生成Gitea Open ID
+ String giteaOpenId = String.valueOf(id);
+
+ // 检查用户是否已存在
+ SysUser sysUser = sysUserService.getUserByGiteaOpenId(giteaOpenId);
+
+ // 如果用户不存在,则创建新用户
+ if (sysUser == null) {
+ sysUser = new SysUser();
+ sysUser.setUsername(username);
+ sysUser.setNickname(username);
+ sysUser.setEmail(email);
+ sysUser.setAvatar(avatarUrl);
+ sysUser.setGiteaOpenId(giteaOpenId);
+ sysUserService.save(sysUser);
+ } else {
+ // 更新用户信息
+ sysUser.setEmail(email);
+ sysUser.setAvatar(avatarUrl);
+ sysUserService.updateById(sysUser);
+ }
+
+ // 使用Sa-Token登录
+ StpUtil.login(sysUser.getUserId());
+ SaSession session = StpUtil.getSession();
+
+ // 构建用户信息
+ UserBase userBase = new UserBase();
+ userBase.setUsername(sysUser.getUsername());
+ userBase.setEmail(sysUser.getEmail());
+ userBase.setAvatarUrl(sysUser.getAvatar());
+ userBase.setId(sysUser.getUserId());
+
+ // 设置权限
+ Set permissionSet = new HashSet<>();
+ permissionSet.add("user");
+ userBase.setPermission(permissionSet);
+
+ // 将用户信息存储至session
+ session.set(SessionConstants.USER_KEY, userBase);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/chinaweal/youfool/course/controller/PageController.java b/src/main/java/com/chinaweal/youfool/course/controller/PageController.java
index 27e79f5..ac44ee7 100644
--- a/src/main/java/com/chinaweal/youfool/course/controller/PageController.java
+++ b/src/main/java/com/chinaweal/youfool/course/controller/PageController.java
@@ -1,5 +1,6 @@
package com.chinaweal.youfool.course.controller;
+import cn.dev33.satoken.stp.StpUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@@ -29,6 +30,9 @@ public class PageController {
*/
@GetMapping("/")
public String index() {
+ if (!StpUtil.isLogin()) {
+ return "redirect:/course/login";
+ }
return "index";
}
@@ -39,6 +43,9 @@ public class PageController {
*/
@GetMapping("/index")
public String indexPage() {
+ if (!StpUtil.isLogin()) {
+ return "redirect:/course/login";
+ }
return "index";
}
}
\ No newline at end of file
diff --git a/src/main/java/com/chinaweal/youfool/course/controller/TestController.java b/src/main/java/com/chinaweal/youfool/course/controller/TestController.java
new file mode 100644
index 0000000..56faf97
--- /dev/null
+++ b/src/main/java/com/chinaweal/youfool/course/controller/TestController.java
@@ -0,0 +1,62 @@
+package com.chinaweal.youfool.course.controller;
+
+import cn.dev33.satoken.stp.StpUtil;
+import com.chinaweal.youfool.framework.springboot.rest.RestResult;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 测试控制器
+ *
+ * @author lroyia
+ * @since 2025/10/24
+ **/
+@Slf4j
+@RestController
+@RequestMapping("/test")
+public class TestController {
+
+ /**
+ * 测试接口,需要登录
+ *
+ * @return 测试结果
+ */
+ @GetMapping("/hello")
+ public RestResult hello() {
+ return RestResult.ok("Hello, " + StpUtil.getLoginIdAsString() + "!");
+ }
+
+ /**
+ * 测试管理员权限
+ *
+ * @return 测试结果
+ */
+ @GetMapping("/admin")
+ public RestResult admin() {
+ StpUtil.checkPermission("admin");
+ return RestResult.ok("Admin access granted!");
+ }
+
+ /**
+ * 测试用户权限
+ *
+ * @return 测试结果
+ */
+ @GetMapping("/user")
+ public RestResult user() {
+ StpUtil.checkPermission("user");
+ return RestResult.ok("User access granted!");
+ }
+
+ /**
+ * 获取当前登录信息
+ *
+ * @return 登录信息
+ */
+ @GetMapping("/info")
+ public RestResult info() {
+ return RestResult.ok("Current login id: " + StpUtil.getLoginIdAsString());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/chinaweal/youfool/course/security/OAuth2LoginFailureHandler.java b/src/main/java/com/chinaweal/youfool/course/security/OAuth2LoginFailureHandler.java
deleted file mode 100644
index 1bd727d..0000000
--- a/src/main/java/com/chinaweal/youfool/course/security/OAuth2LoginFailureHandler.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.chinaweal.youfool.course.security;
-
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.web.authentication.AuthenticationFailureHandler;
-import org.springframework.stereotype.Component;
-
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * OAuth2登录失败处理器
- *
- * @author lroyia
- * @since 2025/10/24
- **/
-@Slf4j
-@Component
-public class OAuth2LoginFailureHandler implements AuthenticationFailureHandler {
-
- @Override
- public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
- AuthenticationException exception) throws IOException, ServletException {
- log.error("OAuth2登录失败: {}", exception.getMessage());
-
- // 重定向到登录页面,带上错误信息
- response.sendRedirect("/course/login?error=true&message=" + java.net.URLEncoder.encode(exception.getMessage(), "UTF-8"));
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/chinaweal/youfool/course/security/OAuth2LoginSuccessHandler.java b/src/main/java/com/chinaweal/youfool/course/security/OAuth2LoginSuccessHandler.java
deleted file mode 100644
index 2030e3c..0000000
--- a/src/main/java/com/chinaweal/youfool/course/security/OAuth2LoginSuccessHandler.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.chinaweal.youfool.course.security;
-
-import cn.dev33.satoken.session.SaSession;
-import cn.dev33.satoken.stp.StpUtil;
-import com.chinaweal.youfool.framework.springboot.user.entity.UserBase;
-import com.chinaweal.youfool.course.common.constants.SessionConstants;
-import com.chinaweal.youfool.course.entity.SysUser;
-import com.chinaweal.youfool.course.service.SysUserService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.oauth2.core.user.OAuth2User;
-import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
-import org.springframework.stereotype.Component;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-
-import jakarta.servlet.ServletException;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * OAuth2登录成功处理器
- *
- * @author lroyia
- * @since 2025/10/24
- **/
-@Slf4j
-@Component
-public class OAuth2LoginSuccessHandler implements AuthenticationSuccessHandler {
-
- @Autowired
- private SysUserService sysUserService;
-
- @Override
- public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
- Authentication authentication) throws IOException, ServletException {
- OAuth2User oauth2User = (OAuth2User) authentication.getPrincipal();
- Map attributes = oauth2User.getAttributes();
-
- // 获取Gitea用户信息
- String username = (String) attributes.get("username");
- String email = (String) attributes.get("email");
- String avatarUrl = (String) attributes.get("avatar_url");
- Integer id = (Integer) attributes.get("id");
-
- log.info("Gitea用户登录成功: username={}, email={}, id={}", username, email, id);
-
- // 生成Gitea Open ID
- String giteaOpenId = String.valueOf(id);
-
- // 检查用户是否已存在
- SysUser sysUser = sysUserService.getUserByGiteaOpenId(giteaOpenId);
-
- // 如果用户不存在,则创建新用户
- if (sysUser == null) {
- sysUser = new SysUser();
- sysUser.setUsername(username);
- sysUser.setNickname(username);
- sysUser.setEmail(email);
- sysUser.setAvatar(avatarUrl);
- sysUser.setGiteaOpenId(giteaOpenId);
- sysUserService.save(sysUser);
- } else {
- // 更新用户信息
- sysUser.setEmail(email);
- sysUser.setAvatar(avatarUrl);
- sysUserService.updateById(sysUser);
- }
-
- // 使用Sa-Token登录
- StpUtil.login(sysUser.getUserId());
- SaSession session = StpUtil.getSession();
-
- // 构建用户信息
- UserBase userBase = new UserBase();
- userBase.setUsername(sysUser.getUsername());
- userBase.setEmail(sysUser.getEmail());
- userBase.setAvatarUrl(sysUser.getAvatar());
- userBase.setId(sysUser.getUserId());
-
- // 设置权限
- Set permissionSet = new HashSet<>();
- permissionSet.add("user");
- userBase.setPermission(permissionSet);
-
- // 将用户信息存储至session
- session.set(SessionConstants.USER_KEY, userBase);
-
- // 重定向到首页
- response.sendRedirect("/course/");
- }
-}
\ No newline at end of file
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 47bb0de..024daf9 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -14,23 +14,14 @@ spring:
suffix: .html
encoding: UTF-8
mode: HTML
- # OAuth2配置
- security:
- oauth2:
- client:
- registration:
- gitea:
- client-id: ${GITEA_CLIENT_ID:your-gitea-client-id}
- client-secret: ${GITEA_CLIENT_SECRET:your-gitea-client-secret}
- authorization-grant-type: authorization_code
- redirect-uri: ${BASE_URL:http://localhost:8080}/course/login/oauth2/code/gitea
- scope: read:user
- provider:
- gitea:
- authorization-uri: ${GITEA_AUTH_URL:https://gitea.com/login/oauth/authorize}
- token-uri: ${GITEA_TOKEN_URL:https://gitea.com/login/oauth/access_token}
- user-info-uri: ${GITEA_USER_URL:https://gitea.com/api/v1/user}
- user-name-attribute: username
+ # Gitea OAuth2配置
+ gitea:
+ client-id: ${GITEA_CLIENT_ID:your-gitea-client-id}
+ client-secret: ${GITEA_CLIENT_SECRET:your-gitea-client-secret}
+ auth-url: ${GITEA_AUTH_URL:https://gitea.com/login/oauth/authorize}
+ token-url: ${GITEA_TOKEN_URL:https://gitea.com/login/oauth/access_token}
+ user-url: ${GITEA_USER_URL:https://gitea.com/api/v1/user}
+ redirect-uri: ${BASE_URL:http://localhost:8080}/course/oauth2/gitea/callback
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组,默认值即为master
diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html
index 78ecd8b..43ee642 100644
--- a/src/main/resources/templates/login.html
+++ b/src/main/resources/templates/login.html
@@ -136,7 +136,7 @@
或
-
+