From 485024c88ecbb0eeb71c8fe3c8b779d225cb4a45 Mon Sep 17 00:00:00 2001 From: qmstyle Date: Sat, 22 Mar 2025 16:23:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E9=AA=8C=E8=AF=81=E7=A0=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 37 +++- .../online/exam/config/LoginSysLog.java | 41 ++++ .../exam/controller/InitController.java | 199 ++++++++++++++++++ .../exam/controller/PaperController.java | 3 + .../VerificationCodeController.java | 23 ++ .../zhangmeng/online/exam/dao/UserDao.java | 3 + .../exam/service/PermissionService.java | 14 ++ .../online/exam/service/UserService.java | 12 ++ .../service/impl/PermissionServiceImpl.java | 49 +++++ .../service/impl/UserDetailsServiceImpl.java | 65 ++++++ .../exam/service/impl/UserServiceImpl.java | 25 +++ .../online/exam/utils/PageUtils.java | 7 + src/main/resources/application.yml | 34 ++- 13 files changed, 499 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/zhangmeng/online/exam/config/LoginSysLog.java create mode 100644 src/main/java/com/zhangmeng/online/exam/controller/VerificationCodeController.java create mode 100644 src/main/java/com/zhangmeng/online/exam/service/PermissionService.java create mode 100644 src/main/java/com/zhangmeng/online/exam/service/UserService.java create mode 100644 src/main/java/com/zhangmeng/online/exam/service/impl/PermissionServiceImpl.java create mode 100644 src/main/java/com/zhangmeng/online/exam/service/impl/UserDetailsServiceImpl.java create mode 100644 src/main/java/com/zhangmeng/online/exam/service/impl/UserServiceImpl.java 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