加入验证码登录逻辑

master
qmstyle 2025-03-22 16:23:25 +08:00
parent 360722a915
commit 485024c88e
13 changed files with 499 additions and 13 deletions

37
pom.xml
View File

@ -16,6 +16,7 @@
<properties>
<java.version>17</java.version>
<swagger.version>2.8.0</swagger.version>
</properties>
<dependencies>
<dependency>
@ -61,15 +62,43 @@
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-security</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>com.zhangmeng</groupId>
<artifactId>jwt-security-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.52</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-bean-validators</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>

View File

@ -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));
}
}

View File

@ -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");
}
}

View File

@ -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> question = this.questionDao.findById(Long.valueOf(questionId));
if (question.isPresent()){

View File

@ -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());
}
}

View File

@ -9,4 +9,7 @@ import org.springframework.data.jpa.repository.JpaRepository;
* @version: 1.0
*/
public interface UserDao extends JpaRepository<User, Long> {
public User findByUsername(String username);
}

View File

@ -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<Permission> findByUserId(Long id);
}

View File

@ -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);
}

View File

@ -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<Permission> findByUserId(Long id) {
Optional<User> userOptional = this.userDao.findById(id);
if (userOptional.isPresent()) {
User user = userOptional.get();
List<Permission> permissions = new ArrayList<>();
Set<Role> roles = user.getRoles();
for (Role role : roles) {
Set<Permission> permissions1 = role.getPermissions();
permissions.addAll(permissions1);
}
return permissions;
}
return null;
}
}

View File

@ -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<Permission> permissions = this.permissionService.findByUserId(user.getId());
return new LoginUser(user.getId(),username,user.getPassword(),this.convert_permissions(permissions));
}
//转换
private List<com.zhangmeng.jwt.dto.Permission> convert_permissions (List<Permission> 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());
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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
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