diff --git a/pom.xml b/pom.xml
index 5639f9a..89e6f38 100644
--- a/pom.xml
+++ b/pom.xml
@@ -16,6 +16,7 @@
17
+ 2.8.0
@@ -61,15 +62,43 @@
spring-boot-starter-freemarker
-
-
-
-
+
+ com.zhangmeng
+ jwt-security-spring-boot-starter
+ 0.0.1-SNAPSHOT
+
+
com.alibaba
fastjson
2.0.52
+
+
+
+ io.springfox
+ springfox-swagger2
+ ${swagger.version}
+
+
+
+ io.springfox
+ springfox-bean-validators
+ ${swagger.version}
+
+
+
+ io.springfox
+ springfox-swagger-ui
+ ${swagger.version}
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
diff --git a/src/main/java/com/zhangmeng/online/exam/config/LoginSysLog.java b/src/main/java/com/zhangmeng/online/exam/config/LoginSysLog.java
new file mode 100644
index 0000000..42124bd
--- /dev/null
+++ b/src/main/java/com/zhangmeng/online/exam/config/LoginSysLog.java
@@ -0,0 +1,41 @@
+package com.zhangmeng.online.exam.config;
+
+import com.zhangmeng.jwt.config.SecurityAuthenticationSuccessHandler;
+import com.zhangmeng.jwt.dto.LoginUser;
+import com.zhangmeng.jwt.dto.Token;
+import com.zhangmeng.jwt.response.Result;
+import com.zhangmeng.jwt.response.StatusCode;
+import com.zhangmeng.jwt.service.TokenService;
+import com.zhangmeng.jwt.util.ResponseUtil;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * @author zm
+ * @date 2025/3/22 15:36
+ * @version: 1.0
+ */
+@Component
+public class LoginSysLog extends SecurityAuthenticationSuccessHandler {
+
+ @Autowired
+ private TokenService tokenService;
+
+ @Override
+ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
+ LoginUser loginUser = (LoginUser) authentication.getPrincipal();
+ Token token = tokenService.saveToken(loginUser);
+ String message = "登录成功";
+ ResponseUtil.responseJson(response, HttpStatus.OK.value(), new Result(true, StatusCode.OK,message, token));
+ }
+}
diff --git a/src/main/java/com/zhangmeng/online/exam/controller/InitController.java b/src/main/java/com/zhangmeng/online/exam/controller/InitController.java
index dfd1bbb..ad4477e 100644
--- a/src/main/java/com/zhangmeng/online/exam/controller/InitController.java
+++ b/src/main/java/com/zhangmeng/online/exam/controller/InitController.java
@@ -714,4 +714,203 @@ public class InitController {
role.getPermissions().add(permission2);
roleDao.save(role);
}
+
+ @GetMapping("/test4")
+ public void test4() {
+ Subject subject = this.subjectDao.findByCode("001");
+
+ Question question = new Question();
+ question.setSubject(subject);
+ question.setType(Question.Type.JUDGMENT);
+ question.setName("JavaFX 是 Oracle 官方提供的 GUI 框架吗?");
+ this.questionDao.save(question);
+
+ QuestionOption optionA = new QuestionOption();
+ optionA.setQuestion(question);
+ optionA.setOptionName("A.");
+ optionA.setOptionContent("正确");
+ optionA.setOptionOrder(1);
+ optionA.setIsAnswer(true);
+ this.questionOptionDao.save(optionA);
+
+ QuestionOption optionB = new QuestionOption();
+ optionB.setQuestion(question);
+ optionB.setOptionName("B.");
+ optionB.setOptionContent("错误");
+ optionB.setOptionOrder(2);
+ optionB.setIsAnswer(false);
+ this.questionOptionDao.save(optionB);
+
+ question.setOptions(Arrays.asList(optionA, optionB));
+
+ question = new Question();
+ question.setSubject(subject);
+ question.setType(Question.Type.JUDGMENT);
+ question.setName("“精兵简政”指精简机构以提高效率,减轻负担。");
+ this.questionDao.save(question);
+
+ optionA = new QuestionOption();
+ optionA.setQuestion(question);
+ optionA.setOptionName("A.");
+ optionA.setOptionContent("正确");
+ optionA.setOptionOrder(1);
+ optionA.setIsAnswer(true);
+ this.questionOptionDao.save(optionA);
+
+ optionB = new QuestionOption();
+ optionB.setQuestion(question);
+ optionB.setOptionName("B.");
+ optionB.setOptionContent("错误");
+ optionB.setOptionOrder(2);
+ optionB.setIsAnswer(false);
+ this.questionOptionDao.save(optionB);
+
+ question.setOptions(Arrays.asList(optionA, optionB));
+
+
+ question = new Question();
+ question.setSubject(subject);
+ question.setType(Question.Type.JUDGMENT);
+ question.setName("“知之为知之,不知为不知,是知也”出自《论语》,与孟子无关。");
+ this.questionDao.save(question);
+
+ optionA = new QuestionOption();
+ optionA.setQuestion(question);
+ optionA.setOptionName("A.");
+ optionA.setOptionContent("正确");
+ optionA.setOptionOrder(1);
+ optionA.setIsAnswer(true);
+ this.questionOptionDao.save(optionA);
+
+ optionB = new QuestionOption();
+ optionB.setQuestion(question);
+ optionB.setOptionName("B.");
+ optionB.setOptionContent("错误");
+ optionB.setOptionOrder(2);
+ optionB.setIsAnswer(false);
+ this.questionOptionDao.save(optionB);
+
+ question.setOptions(Arrays.asList(optionA, optionB));
+
+ /**
+ * 成语“玲珑剔透”可形容器物精致,也可比喻人精明灵活。
+ */
+ question = new Question();
+ question.setSubject(subject);
+ question.setType(Question.Type.JUDGMENT);
+ question.setName("“玲珑剔透”可形容器物精致,也可比喻人精明灵活。");
+ question.setExplanation("仅用于器物");
+ this.questionDao.save(question);
+
+ optionA = new QuestionOption();
+ optionA.setQuestion(question);
+ optionA.setOptionName("A.");
+ optionA.setOptionContent("正确");
+ optionA.setOptionOrder(1);
+ optionA.setIsAnswer(false);
+ this.questionOptionDao.save(optionA);
+
+ optionB = new QuestionOption();
+ optionB.setQuestion(question);
+ optionB.setOptionName("B.");
+ optionB.setOptionContent("错误");
+ optionB.setOptionOrder(2);
+ optionB.setIsAnswer(false);
+ this.questionOptionDao.save(optionB);
+
+ question.setOptions(Arrays.asList(optionA, optionB));
+
+ }
+
+ /**
+ * currentQuestion.content = "简述面向对象编程的三大特性";
+ * currentQuestion.keywords = Arrays.asList("封装", "继承", "多态");
+ */
+ @GetMapping("/test5")
+ public void test6() {
+ Subject subject = this.subjectDao.findByCode("001");
+
+ Question question = new Question();
+ question.setSubject(subject);
+ question.setType(Question.Type.SHORT_ANSWER);
+ question.setName("简述面向对象编程的三大特性");
+ this.questionDao.save(question);
+
+ QuestionOption optionA = new QuestionOption();
+ optionA.setQuestion(question);
+ optionA.setOptionName("A.");
+ optionA.setOptionContent("封装");
+ optionA.setOptionOrder(1);
+ optionA.setIsAnswer(true);
+ this.questionOptionDao.save(optionA);
+
+ QuestionOption optionB = new QuestionOption();
+ optionB.setQuestion(question);
+ optionB.setOptionName("B.");
+ optionB.setOptionContent("继承");
+ optionB.setOptionOrder(2);
+ optionB.setIsAnswer(true);
+ this.questionOptionDao.save(optionB);
+
+ QuestionOption optionC = new QuestionOption();
+ optionC.setQuestion(question);
+ optionC.setOptionName("B.");
+ optionC.setOptionContent("多态");
+ optionC.setOptionOrder(3);
+ optionC.setIsAnswer(true);
+ this.questionOptionDao.save(optionC);
+
+ question.setOptions(Arrays.asList(optionA, optionB, optionC));
+
+ question = new Question();
+ question.setSubject(subject);
+ question.setType(Question.Type.SHORT_ANSWER);
+ question.setName("《朝花夕拾》中鲁迅回忆藤野先生的目的是什么?");
+ this.questionDao.save(question);
+
+ optionA = new QuestionOption();
+ optionA.setQuestion(question);
+ optionA.setOptionName("A.");
+ optionA.setOptionContent("汲取精神力量");
+ optionA.setOptionOrder(1);
+ optionA.setIsAnswer(true);
+ this.questionOptionDao.save(optionA);
+
+ optionB = new QuestionOption();
+ optionB.setQuestion(question);
+ optionB.setOptionName("B.");
+ optionB.setOptionContent("封建势力");
+ optionB.setOptionOrder(2);
+ optionB.setIsAnswer(true);
+ this.questionOptionDao.save(optionB);
+
+ optionC = new QuestionOption();
+ optionC.setQuestion(question);
+ optionC.setOptionName("C.");
+ optionC.setOptionContent("虚伪");
+ optionC.setOptionOrder(3);
+ optionC.setIsAnswer(true);
+ this.questionOptionDao.save(optionC);
+
+ QuestionOption optionD = new QuestionOption();
+ optionD.setQuestion(question);
+ optionD.setOptionName("D.");
+ optionD.setOptionContent("正人君子");
+ optionD.setOptionOrder(4);
+ optionD.setIsAnswer(true);
+ this.questionOptionDao.save(optionD);
+
+ question.setOptions(Arrays.asList(optionA, optionB, optionC,optionD));
+
+
+ }
+
+
+ @GetMapping("/test6")
+ public void test7() {
+ Subject subject = this.subjectDao.findByCode("001");
+
+
+
+ }
}
diff --git a/src/main/java/com/zhangmeng/online/exam/controller/PaperController.java b/src/main/java/com/zhangmeng/online/exam/controller/PaperController.java
index 5893b45..7e1d636 100644
--- a/src/main/java/com/zhangmeng/online/exam/controller/PaperController.java
+++ b/src/main/java/com/zhangmeng/online/exam/controller/PaperController.java
@@ -63,6 +63,9 @@ public class PaperController {
return Result.error("试卷不存在");
}
Paper paper = paperOptional.get();
+
+ paper.getQuestions().clear();
+
for (String questionId : JSONObject.parseArray(ids, String.class)) {
Optional question = this.questionDao.findById(Long.valueOf(questionId));
if (question.isPresent()){
diff --git a/src/main/java/com/zhangmeng/online/exam/controller/VerificationCodeController.java b/src/main/java/com/zhangmeng/online/exam/controller/VerificationCodeController.java
new file mode 100644
index 0000000..58205ae
--- /dev/null
+++ b/src/main/java/com/zhangmeng/online/exam/controller/VerificationCodeController.java
@@ -0,0 +1,23 @@
+package com.zhangmeng.online.exam.controller;
+
+import com.zhangmeng.jwt.factory.VerificationCodeFactory;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+/**
+ * @author zm
+ * @date 2025/3/22 15:56
+ * @version: 1.0
+ */
+@RestController
+@RequestMapping("/verificationCode")
+public class VerificationCodeController {
+
+ @RequestMapping("/generate")
+ public void generate(HttpServletRequest request, HttpServletResponse response) throws Exception {
+ VerificationCodeFactory.getSpecCaptcha().out(response.getOutputStream());
+ }
+
+}
diff --git a/src/main/java/com/zhangmeng/online/exam/dao/UserDao.java b/src/main/java/com/zhangmeng/online/exam/dao/UserDao.java
index d188eb7..852f4dd 100644
--- a/src/main/java/com/zhangmeng/online/exam/dao/UserDao.java
+++ b/src/main/java/com/zhangmeng/online/exam/dao/UserDao.java
@@ -9,4 +9,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
* @version: 1.0
*/
public interface UserDao extends JpaRepository {
+
+ public User findByUsername(String username);
+
}
diff --git a/src/main/java/com/zhangmeng/online/exam/service/PermissionService.java b/src/main/java/com/zhangmeng/online/exam/service/PermissionService.java
new file mode 100644
index 0000000..ddf06c6
--- /dev/null
+++ b/src/main/java/com/zhangmeng/online/exam/service/PermissionService.java
@@ -0,0 +1,14 @@
+package com.zhangmeng.online.exam.service;
+
+import com.zhangmeng.online.exam.entity.Permission;
+
+import java.util.List;
+
+/**
+ * @author zm
+ * @date 2025/3/22 15:43
+ * @version: 1.0
+ */
+public interface PermissionService {
+ List findByUserId(Long id);
+}
diff --git a/src/main/java/com/zhangmeng/online/exam/service/UserService.java b/src/main/java/com/zhangmeng/online/exam/service/UserService.java
new file mode 100644
index 0000000..fdace22
--- /dev/null
+++ b/src/main/java/com/zhangmeng/online/exam/service/UserService.java
@@ -0,0 +1,12 @@
+package com.zhangmeng.online.exam.service;
+
+import com.zhangmeng.online.exam.entity.User;
+
+/**
+ * @author zm
+ * @date 2025/3/22 15:43
+ * @version: 1.0
+ */
+public interface UserService {
+ User loadUserByUsername(String username);
+}
diff --git a/src/main/java/com/zhangmeng/online/exam/service/impl/PermissionServiceImpl.java b/src/main/java/com/zhangmeng/online/exam/service/impl/PermissionServiceImpl.java
new file mode 100644
index 0000000..49ead20
--- /dev/null
+++ b/src/main/java/com/zhangmeng/online/exam/service/impl/PermissionServiceImpl.java
@@ -0,0 +1,49 @@
+package com.zhangmeng.online.exam.service.impl;
+
+import com.zhangmeng.online.exam.dao.PermissionDao;
+import com.zhangmeng.online.exam.dao.UserDao;
+import com.zhangmeng.online.exam.entity.Permission;
+import com.zhangmeng.online.exam.entity.Role;
+import com.zhangmeng.online.exam.entity.User;
+import com.zhangmeng.online.exam.service.PermissionService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @author zm
+ * @date 2025/3/22 15:43
+ * @version: 1.0
+ */
+@Service
+public class PermissionServiceImpl implements PermissionService {
+
+ @Autowired
+ private PermissionDao permissionDao;
+
+ @Autowired
+ private UserDao userDao;
+
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public List findByUserId(Long id) {
+ Optional userOptional = this.userDao.findById(id);
+ if (userOptional.isPresent()) {
+ User user = userOptional.get();
+ List permissions = new ArrayList<>();
+ Set roles = user.getRoles();
+ for (Role role : roles) {
+ Set permissions1 = role.getPermissions();
+ permissions.addAll(permissions1);
+ }
+ return permissions;
+ }
+ return null;
+ }
+
+
+}
diff --git a/src/main/java/com/zhangmeng/online/exam/service/impl/UserDetailsServiceImpl.java b/src/main/java/com/zhangmeng/online/exam/service/impl/UserDetailsServiceImpl.java
new file mode 100644
index 0000000..2b3f722
--- /dev/null
+++ b/src/main/java/com/zhangmeng/online/exam/service/impl/UserDetailsServiceImpl.java
@@ -0,0 +1,65 @@
+package com.zhangmeng.online.exam.service.impl;
+
+import com.zhangmeng.jwt.dto.LoginUser;
+
+import com.zhangmeng.online.exam.entity.Permission;
+import com.zhangmeng.online.exam.entity.User;
+import com.zhangmeng.online.exam.service.PermissionService;
+import com.zhangmeng.online.exam.service.UserService;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.DisabledException;
+import org.springframework.security.authentication.LockedException;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * @author zm
+ * @date 2025/3/22 15:42
+ * @version: 1.0
+ */
+@Service
+public class UserDetailsServiceImpl implements UserDetailsService {
+
+ @Autowired
+ private UserService userService;
+
+ @Autowired
+ private PermissionService permissionService;
+
+ @Override
+ public org.springframework.security.core.userdetails.UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+ User user = this.userService.loadUserByUsername(username);
+ if (user == null) {
+ throw new UsernameNotFoundException("用户未找到");
+ }
+
+ //判断用户状态是否正常
+ if (!user.getDeleteStatus()) {
+ if (user.getStatus().equals(User.Status.LOCKED)) {
+ throw new LockedException("用户被锁定,请联系管理员");
+ }
+ if (user.getStatus().equals(User.Status.DISABLED)) {
+ throw new DisabledException("用户已作废");
+ }
+ }
+ //根据用户查询权限列表
+ List permissions = this.permissionService.findByUserId(user.getId());
+ return new LoginUser(user.getId(),username,user.getPassword(),this.convert_permissions(permissions));
+ }
+
+ //转换
+ private List convert_permissions (List permissions){
+ return permissions.parallelStream().filter(permission -> !StringUtils.isEmpty(permission.getName()))
+ .map(permission -> {
+ com.zhangmeng.jwt.dto.Permission permission1 = new com.zhangmeng.jwt.dto.Permission();
+ permission1.setTitle(permission.getName());
+ return permission1;
+ }).collect(Collectors.toList());
+ }
+}
diff --git a/src/main/java/com/zhangmeng/online/exam/service/impl/UserServiceImpl.java b/src/main/java/com/zhangmeng/online/exam/service/impl/UserServiceImpl.java
new file mode 100644
index 0000000..2c844ee
--- /dev/null
+++ b/src/main/java/com/zhangmeng/online/exam/service/impl/UserServiceImpl.java
@@ -0,0 +1,25 @@
+package com.zhangmeng.online.exam.service.impl;
+
+import com.zhangmeng.online.exam.dao.UserDao;
+import com.zhangmeng.online.exam.entity.User;
+import com.zhangmeng.online.exam.service.UserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author zm
+ * @date 2025/3/22 15:43
+ * @version: 1.0
+ */
+@Service
+public class UserServiceImpl implements UserService {
+
+ @Autowired
+ private UserDao userDao;
+
+
+ @Override
+ public User loadUserByUsername(String username) {
+ return this.userDao.findByUsername(username);
+ }
+}
diff --git a/src/main/java/com/zhangmeng/online/exam/utils/PageUtils.java b/src/main/java/com/zhangmeng/online/exam/utils/PageUtils.java
index c376a6b..a0bc3d7 100644
--- a/src/main/java/com/zhangmeng/online/exam/utils/PageUtils.java
+++ b/src/main/java/com/zhangmeng/online/exam/utils/PageUtils.java
@@ -1,5 +1,7 @@
package com.zhangmeng.online.exam.utils;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
/**
* @author zm
* @date 2025/3/14 16:36
@@ -18,4 +20,9 @@ public class PageUtils {
return pageNum;
}
+ public static void main(String[] args) {
+ BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+ String password = encoder.encode("123456");
+ System.out.println(password);
+ }
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 8628467..f4b6c24 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -19,12 +19,28 @@ spring:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
# format_sql: true
- freemarker:
- suffix: .ftl
- check-template-location: true
- content-type: text/html
- charset: UTF-8
- template-loader-path: classpath:/templates/
- cache: false
- enabled: true
- request-context-attribute: request
\ No newline at end of file
+token:
+ jwt-secret: zhangmeng
+ exper-time: 6000
+jwt:
+ security:
+ login-page: /login
+ login-url: /login
+ logout-url: logout
+ authentication-entry-point-msg: 请先登录后,在进行访问
+ open-api:
+ - /
+ - /login/**
+ - /system/**
+ - /verificationCode/generate
+ - /css/**
+ - /js/**
+ - /editor/**
+ - /swagger-ui.html
+ - /swagger-ui/**
+ - /swagger-resources/**
+ - /v2/api-docs
+ - /v3/api-docs
+ - /doc.html
+ - /webjars/**
+ - /favicon.ico # ??FAVICON
\ No newline at end of file