diff --git a/docs/springSecurity.md b/docs/springSecurity.md new file mode 100644 index 0000000..e7d949d --- /dev/null +++ b/docs/springSecurity.md @@ -0,0 +1,46 @@ +是的,Spring Security 在每次带有 token 的请求中,确实都会走一遍认证流程,但并不一定每次都“存储”认证信息,具体行为取决于你使用的 token 类型(如 JWT、Session、OAuth2 等)和安全配置。 + +一、JWT(常见于无状态登录) +如果你使用的是 JWT(JSON Web Token)这种无状态认证机制: + +每次请求都要带上 token(通常放在 Authorization Header 中) + +Spring Security 会通过一个过滤器(如 OncePerRequestFilter)解析这个 token + +验证 token 的有效性(比如签名、过期时间) + +从 token 中解析用户信息(如用户名、角色),然后在当前请求的 SecurityContext 中构建认证对象 + +请求处理完后,SecurityContext 是线程局部的,不会跨请求存储,下次请求又要重新走这个流程 + +✅ 好处是服务无状态、扩展性强 +❌ 每次请求都要解析和验证 token,有一定性能开销 + +二、Session(默认方式) +如果你使用的是 Spring Security 默认的基于 Session 的认证机制: + +登录成功后服务端会生成一个 SessionId,并将其通过 Cookie 返回给客户端 + +客户端下次请求自动带上 Cookie + +Spring Security 会通过这个 SessionId 直接从服务端 Session 中获取用户的认证信息 + +如果存在有效 Session,就不需要重新认证,不走身份验证流程 + +✅ 好处是性能较高 +❌ 缺点是服务有状态,不利于分布式部署(需共享 Session) + +三、OAuth2 Token(Resource Server 模式) +如果你用的是 OAuth2,比如使用 spring-security-oauth2-resource-server: + +每次请求带上 Bearer Token + +Spring Security 会通过配置好的 JwtDecoder 验证 token(或远程调用 introspection endpoint) + +同样会在请求级别构建一次 Authentication 对象,不会存储 + +总结 +场景 每次验证 Token 认证信息是否持久化 +JWT ✅ 是 ❌ 否,线程局部 +Session ❌ 否,登录一次 ✅ 是,存储在 Session +OAuth2 Resource Server ✅ 是 ❌ 否,线程局部 \ No newline at end of file diff --git a/docs/txt/0509TODO.txt b/docs/txt/0509TODO.txt index a5981a8..f668b5a 100644 --- a/docs/txt/0509TODO.txt +++ b/docs/txt/0509TODO.txt @@ -7,5 +7,3 @@ courseDetail查询时候加入userId 进度表 webChat - -courseCenter View 里面对于course 新增分类 级别 \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/constant/SecurityConstants.java b/src/main/java/com/guwan/backend/constant/SecurityConstants.java index 31c57fb..e2bc233 100644 --- a/src/main/java/com/guwan/backend/constant/SecurityConstants.java +++ b/src/main/java/com/guwan/backend/constant/SecurityConstants.java @@ -19,6 +19,8 @@ public class SecurityConstants { "/bs/user/getPhoneCode", "/bs/user/verifyFace", + "/bs/courses/queryByPage", + "/challenge", "/ws/**", "/faceTest", "/compareFaces", @@ -26,7 +28,7 @@ public class SecurityConstants { "/exam/api/paper/**", - "/bs/courses/**", + "/bs/courses/queryByPage", "/api/common/**", //公共接口 "/demo/**", // 测试接口 "/api/products", diff --git a/src/main/java/com/guwan/backend/mapper/UserCourseRegistrationMapper.java b/src/main/java/com/guwan/backend/mapper/UserCourseRegistrationMapper.java new file mode 100644 index 0000000..3639d6f --- /dev/null +++ b/src/main/java/com/guwan/backend/mapper/UserCourseRegistrationMapper.java @@ -0,0 +1,18 @@ +package com.guwan.backend.mapper; + +import com.guwan.backend.pojo.entity.UserCourseRegistration; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* @author 12455 +* @description 针对表【user_course_registration】的数据库操作Mapper +* @createDate 2025-05-11 19:27:07 +* @Entity com.guwan.backend.pojo.entity.UserCourseRegistration +*/ +public interface UserCourseRegistrationMapper extends BaseMapper { + +} + + + + diff --git a/src/main/java/com/guwan/backend/pojo/entity/UserCourseRegistration.java b/src/main/java/com/guwan/backend/pojo/entity/UserCourseRegistration.java new file mode 100644 index 0000000..0734624 --- /dev/null +++ b/src/main/java/com/guwan/backend/pojo/entity/UserCourseRegistration.java @@ -0,0 +1,30 @@ +package com.guwan.backend.pojo.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * + * @TableName user_course_registration + */ +@TableName(value ="user_course_registration") +@Data +public class UserCourseRegistration { + /** + * + */ + @TableId(type = IdType.AUTO) + private Integer id; + + /** + * + */ + private Integer userId; + + /** + * + */ + private Integer courseId; +} \ No newline at end of file diff --git a/src/main/java/com/guwan/backend/service/UserCourseRegistrationService.java b/src/main/java/com/guwan/backend/service/UserCourseRegistrationService.java new file mode 100644 index 0000000..e4ad853 --- /dev/null +++ b/src/main/java/com/guwan/backend/service/UserCourseRegistrationService.java @@ -0,0 +1,13 @@ +package com.guwan.backend.service; + +import com.guwan.backend.pojo.entity.UserCourseRegistration; +import com.baomidou.mybatisplus.extension.service.IService; + +/** +* @author 12455 +* @description 针对表【user_course_registration】的数据库操作Service +* @createDate 2025-05-11 19:27:07 +*/ +public interface UserCourseRegistrationService extends IService { + +} diff --git a/src/main/java/com/guwan/backend/service/impl/CourseServiceImpl.java b/src/main/java/com/guwan/backend/service/impl/CourseServiceImpl.java index d8f0ace..dde9dca 100644 --- a/src/main/java/com/guwan/backend/service/impl/CourseServiceImpl.java +++ b/src/main/java/com/guwan/backend/service/impl/CourseServiceImpl.java @@ -11,6 +11,7 @@ import com.guwan.backend.pojo.response.courseDetail.ChapterVO; import com.guwan.backend.pojo.response.courseDetail.CourseDetailVO; import com.guwan.backend.pojo.response.courseDetail.ReviewVO; import com.guwan.backend.service.CourseService; +import com.guwan.backend.util.SecurityUtil; import lombok.RequiredArgsConstructor; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; @@ -50,11 +51,17 @@ public class CourseServiceImpl extends ServiceImpl private final CourseTypeMapper courseTypeMapper; + private final SecurityUtil securityUtil; + + private final UserCourseRegistrationMapper userCourseRegistrationMapper; + private static final Map courseStudentCounts = new ConcurrentHashMap<>(); @Override public CourseDetailVO getCourseDetail(String courseId) { + Long currentUserId = securityUtil.getCurrentUserId(); + //首选查询course表 Course course = courseMapper.selectOne(new LambdaQueryWrapper().eq(Course::getId, courseId)); @@ -130,7 +137,12 @@ public class CourseServiceImpl extends ServiceImpl courseDetailVO.setTotalDuration(totalTimeOfMinute / 60); - courseDetailVO.setEnrolled(false); + + UserCourseRegistration userCourseRegistration = userCourseRegistrationMapper.selectOne(new LambdaQueryWrapper() + .eq(UserCourseRegistration::getCourseId, courseId) + .eq(UserCourseRegistration::getUserId, currentUserId)); + + courseDetailVO.setEnrolled(userCourseRegistration != null); courseDetailVO.setCoverImg(course.getCoverImg()); List chapterVOList = new ArrayList<>(); diff --git a/src/main/java/com/guwan/backend/service/impl/UserCourseRegistrationServiceImpl.java b/src/main/java/com/guwan/backend/service/impl/UserCourseRegistrationServiceImpl.java new file mode 100644 index 0000000..ee5d656 --- /dev/null +++ b/src/main/java/com/guwan/backend/service/impl/UserCourseRegistrationServiceImpl.java @@ -0,0 +1,22 @@ +package com.guwan.backend.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.guwan.backend.mapper.UserCourseRegistrationMapper; +import com.guwan.backend.pojo.entity.UserCourseRegistration; +import com.guwan.backend.service.UserCourseRegistrationService; +import org.springframework.stereotype.Service; + +/** +* @author 12455 +* @description 针对表【user_course_registration】的数据库操作Service实现 +* @createDate 2025-05-11 19:27:07 +*/ +@Service +public class UserCourseRegistrationServiceImpl extends ServiceImpl + implements UserCourseRegistrationService { + +} + + + + diff --git a/src/main/resources/mapper/UserCourseRegistrationMapper.xml b/src/main/resources/mapper/UserCourseRegistrationMapper.xml new file mode 100644 index 0000000..ec45fca --- /dev/null +++ b/src/main/resources/mapper/UserCourseRegistrationMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + id,user_id,course_id + +