From 45c0ad0da06895e5d6b7d23e52ef77331c1af287 Mon Sep 17 00:00:00 2001 From: XiaoLFeng Date: Mon, 15 Jan 2024 21:38:40 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=AE=89=E5=85=A8=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E4=B8=8E=E9=82=AE=E4=BB=B6=E7=99=BB=E9=99=86=E6=A8=A1?= =?UTF-8?q?=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + mysql/organize_oa.sql | 2 +- pom.xml | 16 ++++ .../java/com/jsl/oa/config/JwtFilter.java | 81 +++++++++++++++++++ src/main/java/com/jsl/oa/config/JwtToken.java | 23 ------ .../com/jsl/oa/config/MailConfiguration.java | 47 +++++++++++ src/main/java/com/jsl/oa/config/MyRealm.java | 51 ------------ .../java/com/jsl/oa/config/shiro/MyRealm.java | 40 +++++++++ .../{ => shiro}/ShiroConfiguration.java | 14 +++- .../jsl/oa/controllers/AuthController.java | 47 +++++++++-- .../jsl/oa/controllers/CustomController.java | 5 ++ .../jsl/oa/controllers/InfoController.java | 13 --- .../oa/controllers/PermissionController.java | 13 --- .../jsl/oa/controllers/ProjectController.java | 13 --- .../jsl/oa/controllers/RoleController.java | 13 --- src/main/java/com/jsl/oa/dao/UserDAO.java | 9 +++ .../java/com/jsl/oa/mapper/UserMapper.java | 3 + .../com/jsl/oa/model/voData/UserLoginVO.java | 5 +- .../jsl/oa/model/voData/UserRegisterVO.java | 35 ++++---- .../jsl/oa/model/voData/UserReturnBackVO.java | 28 +++++++ .../java/com/jsl/oa/services/AuthService.java | 42 +++++++++- .../java/com/jsl/oa/services/MailService.java | 8 ++ .../jsl/oa/services/impl/AuthServiceImpl.java | 68 +++++++++------- .../jsl/oa/services/impl/MailServiceImpl.java | 47 +++++++++++ src/main/java/com/jsl/oa/utils/ErrorCode.java | 1 + src/main/java/com/jsl/oa/utils/JwtUtil.java | 8 +- .../java/com/jsl/oa/utils/Processing.java | 20 ++++- src/main/resources/.gitkeep | 0 src/main/resources/application.yml | 2 + ...JslOrganizeInternalOaApplicationTests.java | 2 +- 30 files changed, 467 insertions(+), 190 deletions(-) create mode 100644 src/main/java/com/jsl/oa/config/JwtFilter.java delete mode 100644 src/main/java/com/jsl/oa/config/JwtToken.java create mode 100644 src/main/java/com/jsl/oa/config/MailConfiguration.java delete mode 100644 src/main/java/com/jsl/oa/config/MyRealm.java create mode 100644 src/main/java/com/jsl/oa/config/shiro/MyRealm.java rename src/main/java/com/jsl/oa/config/{ => shiro}/ShiroConfiguration.java (70%) create mode 100644 src/main/java/com/jsl/oa/model/voData/UserReturnBackVO.java create mode 100644 src/main/java/com/jsl/oa/services/MailService.java create mode 100644 src/main/java/com/jsl/oa/services/impl/MailServiceImpl.java create mode 100644 src/main/resources/.gitkeep diff --git a/.gitignore b/.gitignore index 48e6007..feff169 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ build/ ### 自定义 ### *.pdf +/src/main/resources/application-dev.yml diff --git a/mysql/organize_oa.sql b/mysql/organize_oa.sql index 0b37dd7..8d09fa0 100644 --- a/mysql/organize_oa.sql +++ b/mysql/organize_oa.sql @@ -195,7 +195,7 @@ CREATE TABLE `oa_user` `account_no_expired` tinyint(1) NOT NULL DEFAULT '1' COMMENT '账户是否过期', `credentials_no_expired` tinyint(1) NOT NULL DEFAULT '0' COMMENT '密码是否过期', `recommend` tinyint(1) NOT NULL DEFAULT '0' COMMENT '账户是否被推荐', - `account_no_locked` tinyint(1) NOT NULL COMMENT '账户是否被锁定', + `account_no_locked` tinyint(1) NOT NULL DEFAULT '1' COMMENT '账户是否被锁定', `description` text COMMENT '个人简介', `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `updated_at` timestamp NULL DEFAULT NULL COMMENT '更新时间' diff --git a/pom.xml b/pom.xml index 0292c13..32fe857 100644 --- a/pom.xml +++ b/pom.xml @@ -103,6 +103,22 @@ org.springframework.boot spring-boot-starter-data-redis + + org.jetbrains + annotations + 24.1.0 + compile + + + + org.springframework.boot + spring-boot-starter-mail + + + + com.google.code.gson + gson + diff --git a/src/main/java/com/jsl/oa/config/JwtFilter.java b/src/main/java/com/jsl/oa/config/JwtFilter.java new file mode 100644 index 0000000..bfa2883 --- /dev/null +++ b/src/main/java/com/jsl/oa/config/JwtFilter.java @@ -0,0 +1,81 @@ +package com.jsl.oa.config; + +import com.google.gson.Gson; +import com.jsl.oa.utils.ErrorCode; +import com.jsl.oa.utils.JwtUtil; +import com.jsl.oa.utils.ResultUtil; +import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter; +import org.jetbrains.annotations.NotNull; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; + +/** + *

JWT过滤器

+ *
+ * 用于JWT的过滤器 + * + * @since v1.1.0 + * @version v1.1.0 + * @author 筱锋xiao_lfeng + */ +public class JwtFilter extends BasicHttpAuthenticationFilter { + + /** + *

判断用户Token

+ *
+ * 判断用户Token是否存在,如果存在则进行验证 + * + * @param request 请求 + * @param response 响应 + * @param mappedValue 映射值 + * @return {@link Boolean} + */ + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + // 判断是否存在Authorization Header + String token = getAuthzHeader(request); + if (token == null || token.isEmpty()) { + return false; // 未提供Token,拒绝访问 + } else { + // 解析Bearer后面的令牌 + token = token.replace("Bearer ", ""); + System.out.println(token); + return JwtUtil.verify(token); + } + } + + /** + *

访问被拒绝时

+ *
+ * 当访问被拒绝时,会调用此方法 + * + * @param request 请求 + * @param response 响应 + * @param mappedValue 映射值 + * @return {@link Boolean} + * @throws Exception 异常 + */ + @Override + protected boolean onAccessDenied(ServletRequest request, @NotNull ServletResponse response, Object mappedValue) throws Exception { + Gson gson = new Gson(); + response.setContentType("application/json;charset=UTF-8"); + response.getWriter().println(gson.toJson(ResultUtil.error(ErrorCode.UNAUTHORIZED))); + return false; + } + + /** + *

获取Authorization Header

+ *
+ * 用于获取Authorization Header + * + * @param request 请求 + * @return {@link String} + */ + @Override + protected String getAuthzHeader(ServletRequest request) { + HttpServletRequest httpRequest = (HttpServletRequest) request; + return httpRequest.getHeader("Authorization"); + } +} diff --git a/src/main/java/com/jsl/oa/config/JwtToken.java b/src/main/java/com/jsl/oa/config/JwtToken.java deleted file mode 100644 index c3dde62..0000000 --- a/src/main/java/com/jsl/oa/config/JwtToken.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.jsl.oa.config; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import org.apache.shiro.authc.AuthenticationToken; - -@Getter -@RequiredArgsConstructor -public class JwtToken implements AuthenticationToken { - - private final String token; - private final String username; - - @Override - public Object getPrincipal() { - return token; - } - - @Override - public Object getCredentials() { - return token; - } -} \ No newline at end of file diff --git a/src/main/java/com/jsl/oa/config/MailConfiguration.java b/src/main/java/com/jsl/oa/config/MailConfiguration.java new file mode 100644 index 0000000..432255f --- /dev/null +++ b/src/main/java/com/jsl/oa/config/MailConfiguration.java @@ -0,0 +1,47 @@ +package com.jsl.oa.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; + +import java.util.Properties; + +/** + *

邮件配置类

+ *
+ * 用于配置邮件发送相关信息 + * + * @since v1.1.0 + * @version v1.1.0 + * @author 筱锋xiao_lfeng + */ +@Configuration +public class MailConfiguration { + + @Value("${spring.mail.host}") + private String emailHost; + @Value("${spring.mail.username}") + private String emailUsername; + @Value("${spring.mail.password}") + private String emailPassword; + + @Bean + public JavaMailSender javaMailSender() { + JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); + mailSender.setDefaultEncoding("UTF-8"); + mailSender.setHost(emailHost); + mailSender.setPort(25); // 你的邮件服务器端口 + mailSender.setUsername(emailUsername); + mailSender.setPassword(emailPassword); + + Properties props = mailSender.getJavaMailProperties(); + props.put("mail.transport.protocol", "smtp"); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.starttls.enable", "true"); + props.put("mail.debug", "true"); + + return mailSender; + } +} diff --git a/src/main/java/com/jsl/oa/config/MyRealm.java b/src/main/java/com/jsl/oa/config/MyRealm.java deleted file mode 100644 index 740eed8..0000000 --- a/src/main/java/com/jsl/oa/config/MyRealm.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.jsl.oa.config; - -import com.jsl.oa.model.doData.UserDO; -import com.jsl.oa.services.UserService; -import lombok.RequiredArgsConstructor; -import org.apache.shiro.authc.*; -import org.apache.shiro.authz.AuthorizationInfo; -import org.apache.shiro.realm.AuthorizingRealm; -import org.apache.shiro.subject.PrincipalCollection; - -@RequiredArgsConstructor -public class MyRealm extends AuthorizingRealm { - - private final UserService userService; - - /** - * 授权 - * @param principalCollection 令牌 - * @return 授权信息 - */ - @Override - protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { - return null; - } - - /** - * 认证 - * @param authenticationToken 令牌 - * @return 认证信息 - * @throws AuthenticationException 认证异常 - */ - @Override - protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { - JwtToken jwtToken = (JwtToken) authenticationToken; - String username = jwtToken.getUsername(); - - // 从数据库获取用户信息 - UserDO userDO = userService.getUserInfoByUsername(username); - if (userDO == null) { - throw new UnknownAccountException("用户不存在"); - } else if (!userDO.getAccountNoLocked()) { - throw new LockedAccountException("用户已被锁定"); - } else if (!userDO.getEnabled()) { - throw new DisabledAccountException("用户已被禁用"); - } else if (!userDO.getAccountNoExpired()) { - throw new ExpiredCredentialsException("用户已过期"); - } - - return new SimpleAuthenticationInfo(username, jwtToken.getCredentials(), getName()); - } -} diff --git a/src/main/java/com/jsl/oa/config/shiro/MyRealm.java b/src/main/java/com/jsl/oa/config/shiro/MyRealm.java new file mode 100644 index 0000000..78e4ff6 --- /dev/null +++ b/src/main/java/com/jsl/oa/config/shiro/MyRealm.java @@ -0,0 +1,40 @@ +package com.jsl.oa.config.shiro; + +import com.jsl.oa.services.UserService; +import lombok.RequiredArgsConstructor; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; +import org.jetbrains.annotations.NotNull; + +@RequiredArgsConstructor +public class MyRealm extends AuthorizingRealm { + + private final UserService userService; + + /** + * 授权 + * + * @return 授权信息 + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(@NotNull PrincipalCollection principals) { + return null; + } + + + /** + * 认证 + * + * @param authenticationToken 令牌 + * @return 认证信息 + * @throws AuthenticationException 认证异常 + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) { + return null; + } +} diff --git a/src/main/java/com/jsl/oa/config/ShiroConfiguration.java b/src/main/java/com/jsl/oa/config/shiro/ShiroConfiguration.java similarity index 70% rename from src/main/java/com/jsl/oa/config/ShiroConfiguration.java rename to src/main/java/com/jsl/oa/config/shiro/ShiroConfiguration.java index ec861a6..b1779e2 100644 --- a/src/main/java/com/jsl/oa/config/ShiroConfiguration.java +++ b/src/main/java/com/jsl/oa/config/shiro/ShiroConfiguration.java @@ -1,5 +1,6 @@ -package com.jsl.oa.config; +package com.jsl.oa.config.shiro; +import com.jsl.oa.config.JwtFilter; import com.jsl.oa.services.UserService; import lombok.RequiredArgsConstructor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; @@ -7,6 +8,7 @@ import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import javax.servlet.Filter; import java.util.LinkedHashMap; import java.util.Map; @@ -24,11 +26,19 @@ public class ShiroConfiguration { // 配置过滤器规则 Map filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/auth/**", "anon"); // 登录接口允许匿名访问 + filterChainDefinitionMap.put("/unauthorized", "anon"); // 未授权接口允许匿名访问 + filterChainDefinitionMap.put("/", "anon"); // 首页允许匿名访问 + filterChainDefinitionMap.put("/**/**", "jwt"); // 其他接口一律拦截(需要Token) shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); - // 设置登录接口 + // 设置未登陆响应接口 shiroFilterFactoryBean.setLoginUrl("/unauthorized"); + + // 添加JWT过滤器 + Map filters = new LinkedHashMap<>(); + filters.put("jwt", new JwtFilter()); // 配置自定义的JWT过滤器 + shiroFilterFactoryBean.setFilters(filters); return shiroFilterFactoryBean; } diff --git a/src/main/java/com/jsl/oa/controllers/AuthController.java b/src/main/java/com/jsl/oa/controllers/AuthController.java index c7bb20a..cfbd6fd 100644 --- a/src/main/java/com/jsl/oa/controllers/AuthController.java +++ b/src/main/java/com/jsl/oa/controllers/AuthController.java @@ -8,18 +8,19 @@ import com.jsl.oa.utils.ErrorCode; import com.jsl.oa.utils.Processing; import com.jsl.oa.utils.ResultUtil; import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.text.ParseException; +import java.util.regex.Pattern; /** *

用户认证控制器

*
* 用户认证控制器,包含用户注册、用户登录、用户登出接口 * - * @since v1.0.0 * @version v1.1.0 * @see AuthService * @see UserRegisterVO @@ -28,6 +29,7 @@ import java.text.ParseException; * @see ErrorCode * @see Processing * @see ResultUtil + * @since v1.0.0 */ @RestController @RequiredArgsConstructor @@ -39,12 +41,12 @@ public class AuthController { *
* 用户注册接口 * - * @since v1.0.0 * @return {@link BaseResponse} * @author 筱锋xiao_lfeng + * @since v1.0.0 */ @PostMapping("/auth/register") - public BaseResponse authRegister(@RequestBody @Validated UserRegisterVO userRegisterVO, BindingResult bindingResult) throws ParseException { + public BaseResponse authRegister(@RequestBody @Validated UserRegisterVO userRegisterVO, @NotNull BindingResult bindingResult) throws ParseException { // 判断是否有参数错误 if (bindingResult.hasErrors()) { return ResultUtil.error(ErrorCode.REQUEST_BODY_ERROR, Processing.getValidatedErrorList(bindingResult)); @@ -57,13 +59,14 @@ public class AuthController { *
* 用户登录接口 * - * @since v1.0.0 - * @param userLoginVO 用户登录信息 + * @param userLoginVO 用户登录信息 + * @param bindingResult 参数校验结果 * @return {@link BaseResponse} * @author 176yunxuan + * @since v1.0.0 */ @GetMapping("/auth/login") - public BaseResponse authLogin(@RequestBody @Validated UserLoginVO userLoginVO, BindingResult bindingResult){ + public BaseResponse authLogin(@RequestBody @Validated UserLoginVO userLoginVO, @NotNull BindingResult bindingResult) { // 判断是否有参数错误 if (bindingResult.hasErrors()) { return ResultUtil.error(ErrorCode.REQUEST_BODY_ERROR, Processing.getValidatedErrorList(bindingResult)); @@ -71,6 +74,38 @@ public class AuthController { return authService.authLogin(userLoginVO); } + /** + *

用户邮箱登录

+ *
+ * 用户邮箱登录接口 + * + * @param email 用户登陆邮箱 + * @return {@link BaseResponse} + * @author 筱锋xiao_lfeng + * @since v1.1.0 + */ + @GetMapping("/auth/login/email") + public BaseResponse authLoginByEmail(@RequestParam String email) { + if (email != null) { + if (Pattern.matches("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", email)) { + return authService.authLoginByEmail(email); + } else { + return ResultUtil.error(ErrorCode.PARAMETER_ERROR); + } + } else { + return ResultUtil.error(ErrorCode.PARAMETER_ERROR); + } + } + + /** + *

用户登出

+ *
+ * 用户登出接口 + * + * @return {@link BaseResponse} + * @since v1.1.0 + */ + @GetMapping("/auth/logout") public BaseResponse authLogout() { return null; } diff --git a/src/main/java/com/jsl/oa/controllers/CustomController.java b/src/main/java/com/jsl/oa/controllers/CustomController.java index 7f62c84..e6535d9 100644 --- a/src/main/java/com/jsl/oa/controllers/CustomController.java +++ b/src/main/java/com/jsl/oa/controllers/CustomController.java @@ -1,15 +1,20 @@ package com.jsl.oa.controllers; +import com.jsl.oa.services.MailService; import com.jsl.oa.utils.BaseResponse; import com.jsl.oa.utils.ResultUtil; +import lombok.RequiredArgsConstructor; import org.springframework.boot.web.servlet.error.ErrorController; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController +@RequiredArgsConstructor public class CustomController implements ErrorController { + private final MailService mailService; + @RequestMapping("/") public BaseResponse index() { return ResultUtil.success("欢迎使用JSL-OA系统,服务器处于正常状态"); diff --git a/src/main/java/com/jsl/oa/controllers/InfoController.java b/src/main/java/com/jsl/oa/controllers/InfoController.java index 0fb29d6..008c89a 100644 --- a/src/main/java/com/jsl/oa/controllers/InfoController.java +++ b/src/main/java/com/jsl/oa/controllers/InfoController.java @@ -1,21 +1,8 @@ package com.jsl.oa.controllers; -import com.jsl.oa.model.voData.UserLoginVO; -import com.jsl.oa.model.voData.UserRegisterVO; -import com.jsl.oa.services.AuthService; -import com.jsl.oa.utils.BaseResponse; -import com.jsl.oa.utils.ErrorCode; -import com.jsl.oa.utils.Processing; -import com.jsl.oa.utils.ResultUtil; import lombok.RequiredArgsConstructor; -import org.springframework.validation.BindingResult; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import java.text.ParseException; - @RestController @RequiredArgsConstructor public class InfoController { diff --git a/src/main/java/com/jsl/oa/controllers/PermissionController.java b/src/main/java/com/jsl/oa/controllers/PermissionController.java index 64ea36d..b305e0c 100644 --- a/src/main/java/com/jsl/oa/controllers/PermissionController.java +++ b/src/main/java/com/jsl/oa/controllers/PermissionController.java @@ -1,21 +1,8 @@ package com.jsl.oa.controllers; -import com.jsl.oa.model.voData.UserLoginVO; -import com.jsl.oa.model.voData.UserRegisterVO; -import com.jsl.oa.services.AuthService; -import com.jsl.oa.utils.BaseResponse; -import com.jsl.oa.utils.ErrorCode; -import com.jsl.oa.utils.Processing; -import com.jsl.oa.utils.ResultUtil; import lombok.RequiredArgsConstructor; -import org.springframework.validation.BindingResult; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import java.text.ParseException; - @RestController @RequiredArgsConstructor public class PermissionController { diff --git a/src/main/java/com/jsl/oa/controllers/ProjectController.java b/src/main/java/com/jsl/oa/controllers/ProjectController.java index 3e1ac22..ee41730 100644 --- a/src/main/java/com/jsl/oa/controllers/ProjectController.java +++ b/src/main/java/com/jsl/oa/controllers/ProjectController.java @@ -1,21 +1,8 @@ package com.jsl.oa.controllers; -import com.jsl.oa.model.voData.UserLoginVO; -import com.jsl.oa.model.voData.UserRegisterVO; -import com.jsl.oa.services.AuthService; -import com.jsl.oa.utils.BaseResponse; -import com.jsl.oa.utils.ErrorCode; -import com.jsl.oa.utils.Processing; -import com.jsl.oa.utils.ResultUtil; import lombok.RequiredArgsConstructor; -import org.springframework.validation.BindingResult; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import java.text.ParseException; - @RestController @RequiredArgsConstructor public class ProjectController { diff --git a/src/main/java/com/jsl/oa/controllers/RoleController.java b/src/main/java/com/jsl/oa/controllers/RoleController.java index 3f0df63..8466221 100644 --- a/src/main/java/com/jsl/oa/controllers/RoleController.java +++ b/src/main/java/com/jsl/oa/controllers/RoleController.java @@ -1,21 +1,8 @@ package com.jsl.oa.controllers; -import com.jsl.oa.model.voData.UserLoginVO; -import com.jsl.oa.model.voData.UserRegisterVO; -import com.jsl.oa.services.AuthService; -import com.jsl.oa.utils.BaseResponse; -import com.jsl.oa.utils.ErrorCode; -import com.jsl.oa.utils.Processing; -import com.jsl.oa.utils.ResultUtil; import lombok.RequiredArgsConstructor; -import org.springframework.validation.BindingResult; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import java.text.ParseException; - @RestController @RequiredArgsConstructor diff --git a/src/main/java/com/jsl/oa/dao/UserDAO.java b/src/main/java/com/jsl/oa/dao/UserDAO.java index 4c0357f..d26c795 100644 --- a/src/main/java/com/jsl/oa/dao/UserDAO.java +++ b/src/main/java/com/jsl/oa/dao/UserDAO.java @@ -13,6 +13,15 @@ public class UserDAO { private final UserMapper userMapper; + /** + *

用户名获取用户信息

+ *
+ * 根据用户名获取用户信息 + * + * @param username 用户名 + * @author 筱锋xiao_lfeng + * @return {@link UserDO} + */ public UserDO getUserInfoByUsername(String username) { UserDO userDO = null; // 从 Redis 获取数据 diff --git a/src/main/java/com/jsl/oa/mapper/UserMapper.java b/src/main/java/com/jsl/oa/mapper/UserMapper.java index da5c88d..8de0aa9 100644 --- a/src/main/java/com/jsl/oa/mapper/UserMapper.java +++ b/src/main/java/com/jsl/oa/mapper/UserMapper.java @@ -37,4 +37,7 @@ public interface UserMapper { @Select("select * from organize_oa.oa_user where id = #{id}") UserDO getUserById(Long id); + + @Select("select * from organize_oa.oa_user where email = #{email}") + UserDO getUserInfoByEmail(String email); } diff --git a/src/main/java/com/jsl/oa/model/voData/UserLoginVO.java b/src/main/java/com/jsl/oa/model/voData/UserLoginVO.java index 25c4ce6..f62e1f8 100644 --- a/src/main/java/com/jsl/oa/model/voData/UserLoginVO.java +++ b/src/main/java/com/jsl/oa/model/voData/UserLoginVO.java @@ -16,8 +16,9 @@ import javax.validation.constraints.Pattern; */ @Getter public class UserLoginVO { - @Pattern(regexp = "^[0-9A-Z]+$", message = "工号格式错误") - private String jobId; + @Pattern(regexp = "^[0-9A-Za-z_]+$", message = "支持用户名/手机号/工号登陆") + @NotBlank(message = "用户名不能为空") + private String user; @NotBlank(message = "密码不能为空") private String password; } diff --git a/src/main/java/com/jsl/oa/model/voData/UserRegisterVO.java b/src/main/java/com/jsl/oa/model/voData/UserRegisterVO.java index 6745d96..7b6df82 100644 --- a/src/main/java/com/jsl/oa/model/voData/UserRegisterVO.java +++ b/src/main/java/com/jsl/oa/model/voData/UserRegisterVO.java @@ -1,9 +1,9 @@ package com.jsl.oa.model.voData; import lombok.Getter; +import lombok.Setter; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Pattern; +import javax.validation.constraints.*; /** *

用户注册自定义实体类

@@ -12,29 +12,34 @@ import javax.validation.constraints.Pattern; * * @author 筱锋xiao_lfeng * @version v1.0.0 - * @since v1.0.0 + * @since v1.1.0 */ @Getter +@Setter public class UserRegisterVO { @NotBlank(message = "用户名不能为空") - @Pattern(regexp = "^[\\u4e00-\\u9fa5]{2,5}$", message = "用户名只能为字母、数字或下划线") + @Pattern(regexp = "^[0-9A-Za-z_]{3,40}$", message = "用户名只能为字母、数字或下划线") private String username; @NotBlank(message = "密码不能为空") private String password; - @Pattern(regexp = "^[012]$", message = "保密:0,男:1,女:2") + @NotBlank(message = "家乡不能为空") + private String address; + + @NotBlank(message = "电话不能为空") + @Pattern(regexp = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$", message = "电话格式错误") + private String phone; + + @NotBlank(message = "邮箱不能为空") + @Pattern(regexp = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$", message = "邮箱格式错误") + private String email; + + @Min(value = 0, message = "保密:0,男:1,女:2") + @Max(value = 2, message = "保密:0,男:1,女:2") + @NotNull(message = "性别不能为空") private Short sex; - @NotBlank(message = "年龄不能为空") + @NotNull(message = "年龄不能为空") private Short age; - - @NotBlank(message = "单位不能为空") - private String unit; - - @NotBlank(message = "职位/专业不能为空") - private String filed; - - @NotBlank(message = "家乡不能为空") - private String hometown; } diff --git a/src/main/java/com/jsl/oa/model/voData/UserReturnBackVO.java b/src/main/java/com/jsl/oa/model/voData/UserReturnBackVO.java new file mode 100644 index 0000000..55aaf1e --- /dev/null +++ b/src/main/java/com/jsl/oa/model/voData/UserReturnBackVO.java @@ -0,0 +1,28 @@ +package com.jsl.oa.model.voData; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +/** + *

用户注册成功UserDO自定义实体类

+ *
+ * 用于处理用户注册表单输出的数据 + * + * @author 筱锋xiao_lfeng + * @version v1.1.0 + * @since v1.1.0 + */ +@Getter +@Setter +@Accessors(chain = true) +public class UserReturnBackVO { + private String jobId; + private String username; + private String address; + private String phone; + private String email; + private Short age; + private Short sex; + private String token; +} diff --git a/src/main/java/com/jsl/oa/services/AuthService.java b/src/main/java/com/jsl/oa/services/AuthService.java index f9a9132..6b4fb1c 100644 --- a/src/main/java/com/jsl/oa/services/AuthService.java +++ b/src/main/java/com/jsl/oa/services/AuthService.java @@ -4,9 +4,45 @@ import com.jsl.oa.model.voData.UserLoginVO; import com.jsl.oa.model.voData.UserRegisterVO; import com.jsl.oa.utils.BaseResponse; -import java.text.ParseException; - +/** + *

用户认证服务接口

+ *
+ * 用户认证服务接口,包含用户注册、用户登录、用户登出接口 + * + * @version v1.1.0 + * @since v1.0.0 + */ public interface AuthService { - BaseResponse authRegister(UserRegisterVO userRegisterVO) throws ParseException; + /** + *

用户注册

+ *
+ * 用户注册服务类操作 + * + * @param userRegisterVO 用户注册信息 + * @return {@link BaseResponse} + * @author 筱锋xiao_lfeng + */ + BaseResponse authRegister(UserRegisterVO userRegisterVO); + + /** + *

用户登录

+ *
+ * 用户登录服务类操作 + * + * @param userLoginVO 用户登录信息 + * @return {@link BaseResponse} + * @author 176yunxuan + */ BaseResponse authLogin(UserLoginVO userLoginVO); + + /** + *

用户登出

+ *
+ * 用户登出服务类操作 + * + * @param email 用户邮箱 + * @return {@link BaseResponse} + * @author 筱锋xiao_lfeng + */ + BaseResponse authLoginByEmail(String email); } diff --git a/src/main/java/com/jsl/oa/services/MailService.java b/src/main/java/com/jsl/oa/services/MailService.java new file mode 100644 index 0000000..36e8153 --- /dev/null +++ b/src/main/java/com/jsl/oa/services/MailService.java @@ -0,0 +1,8 @@ +package com.jsl.oa.services; + +import javax.mail.MessagingException; + +public interface MailService { + void sendMail() throws MessagingException; +} + diff --git a/src/main/java/com/jsl/oa/services/impl/AuthServiceImpl.java b/src/main/java/com/jsl/oa/services/impl/AuthServiceImpl.java index a282f0b..7f44354 100644 --- a/src/main/java/com/jsl/oa/services/impl/AuthServiceImpl.java +++ b/src/main/java/com/jsl/oa/services/impl/AuthServiceImpl.java @@ -2,57 +2,55 @@ package com.jsl.oa.services.impl; import com.jsl.oa.model.doData.UserDO; import com.jsl.oa.model.voData.UserLoginVO; +import com.jsl.oa.model.voData.UserReturnBackVO; import com.jsl.oa.model.voData.UserRegisterVO; import com.jsl.oa.exception.BusinessException; import com.jsl.oa.mapper.UserMapper; import com.jsl.oa.services.AuthService; -import com.jsl.oa.utils.BaseResponse; -import com.jsl.oa.utils.ErrorCode; -import com.jsl.oa.utils.Processing; -import com.jsl.oa.utils.ResultUtil; +import com.jsl.oa.utils.*; import lombok.RequiredArgsConstructor; +import org.jetbrains.annotations.NotNull; import org.mindrot.jbcrypt.BCrypt; import org.springframework.stereotype.Service; -import java.text.ParseException; - +/** + *

用户认证服务实现类

+ *
+ * 用户认证服务实现类,包含用户注册、用户登录、用户登出接口 + * + * @since v1.0.0 + * @version v1.1.0 + * @see AuthService + */ @Service @RequiredArgsConstructor public class AuthServiceImpl implements AuthService { private final UserMapper userMapper; - /** - *

用户注册

- *
- * 用户注册服务类操作 - * - * @param userRegisterVO 用户注册信息 - * @return {@link BaseResponse} - * @throws ParseException 日期转换异常 - */ @Override public BaseResponse authRegister(UserRegisterVO userRegisterVO) { - // 用户检查是否存在 + // 检查用户说是否存在 UserDO getUserByUsername = userMapper.getUserInfoByUsername(userRegisterVO.getUsername()); // 用户名已存在 if (getUserByUsername != null) { return ResultUtil.error(ErrorCode.USER_EXIST); } - // 生成工号 String userNum; do { userNum = Processing.createJobNumber((short) 2); } while (userMapper.getUserByUserNum(userNum) != null); - // 处理性别 // 数据上传 UserDO userDO = new UserDO(); userDO.setJobId(userNum) .setUsername(userRegisterVO.getUsername()) .setPassword(BCrypt.hashpw(userRegisterVO.getPassword(), BCrypt.gensalt())) - .setSex(userRegisterVO.getSex()) - .setAge(userRegisterVO.getAge()); + .setAddress(userRegisterVO.getAddress()) + .setPhone(userRegisterVO.getPhone()) + .setEmail(userRegisterVO.getEmail()) + .setAge(userRegisterVO.getAge()) + .setSex(userRegisterVO.getSex()); // 插入数据 if (userMapper.insertUser(userDO)) { userDO.setPassword(null); @@ -63,15 +61,31 @@ public class AuthServiceImpl implements AuthService { } @Override - public BaseResponse authLogin(UserLoginVO userLoginVO) { - String pwd = userLoginVO.getPassword(); - String encodePwd = userMapper.loginPassword(userLoginVO); - if (encodePwd == null) { + public BaseResponse authLogin(@NotNull UserLoginVO userLoginVO) { + // 检查用户是否存在 + UserDO userDO = userMapper.getUserInfoByUsername(userLoginVO.getUser()); + if (userDO != null) { + // 获取用户并登陆 + if (BCrypt.checkpw(userLoginVO.getPassword(), userDO.getPassword())) { + UserReturnBackVO userReturnBackVO = new UserReturnBackVO(); + // 授权 Token + String token = JwtUtil.generateToken(userDO.getUsername()); + userReturnBackVO.setAddress(userDO.getAddress()) + .setAge(userDO.getAge()) + .setEmail(userDO.getEmail()) + .setJobId(userDO.getJobId()) + .setPhone(userDO.getPhone()) + .setSex(userDO.getSex()) + .setUsername(userDO.getUsername()) + .setToken(token); + return ResultUtil.success("登陆成功", userReturnBackVO); + } else { + return ResultUtil.error(ErrorCode.WRONG_PASSWORD); + } + } else { return ResultUtil.error(ErrorCode.USER_NOT_EXIST); } - if (BCrypt.checkpw(pwd, encodePwd)) { - return ResultUtil.success("登陆成功", userMapper.login(userLoginVO)); - } else return ResultUtil.error(ErrorCode.WRONG_PASSWORD); + } } } diff --git a/src/main/java/com/jsl/oa/services/impl/MailServiceImpl.java b/src/main/java/com/jsl/oa/services/impl/MailServiceImpl.java new file mode 100644 index 0000000..51b5515 --- /dev/null +++ b/src/main/java/com/jsl/oa/services/impl/MailServiceImpl.java @@ -0,0 +1,47 @@ +package com.jsl.oa.services.impl; + +import com.jsl.oa.services.MailService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; + +import javax.mail.internet.MimeMessage; + +@Service +@RequiredArgsConstructor +public class MailServiceImpl implements MailService { + private final JavaMailSender javaMailSender; + + @Value("${spring.mail.username}") + private String from; + + + @Override + public void sendMail() { + //发送多媒体邮件 + try { + MimeMessage message = javaMailSender.createMimeMessage(); + + //第二个参数控制着附件上传 + MimeMessageHelper helper = new MimeMessageHelper(message, true); + + helper.setFrom(from); + String to = "lfengzeng@vip.qq.com"; + helper.setTo(to); + String subject = "springboot测试邮件"; + helper.setSubject(subject); + + //第二个参数表示以 html 语法解析文本 + String text = "你好:这是一封测试邮件。请坚持下去。加油!"; + helper.setText(text, true); + + javaMailSender.send(message); + + } catch (Exception e) { + //TODO: 10001-发送邮件失败处理 + } + + } +} diff --git a/src/main/java/com/jsl/oa/utils/ErrorCode.java b/src/main/java/com/jsl/oa/utils/ErrorCode.java index 02b5895..a13ea59 100644 --- a/src/main/java/com/jsl/oa/utils/ErrorCode.java +++ b/src/main/java/com/jsl/oa/utils/ErrorCode.java @@ -10,6 +10,7 @@ public enum ErrorCode { USER_EXIST("UserExist", 40013, "用户名已存在"), TIMESTAMP_ERROR("TimestampError", 40014, "时间戳错误"), USER_NOT_EXIST("UserNotExist", 40015, "用户不存在"), + UNAUTHORIZED("Unauthorized", 40100, "未授权"), DATABASE_INSERT_ERROR("DatabaseInsertError", 50010, "数据库插入错误"), DATABASE_UPDATE_ERROR("DatabaseUpdateError", 50011, "数据库更新错误"), DATABASE_DELETE_ERROR("DatabaseDeleteError", 50012, "数据库删除错误"); diff --git a/src/main/java/com/jsl/oa/utils/JwtUtil.java b/src/main/java/com/jsl/oa/utils/JwtUtil.java index adf9801..464d04a 100644 --- a/src/main/java/com/jsl/oa/utils/JwtUtil.java +++ b/src/main/java/com/jsl/oa/utils/JwtUtil.java @@ -7,10 +7,11 @@ import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; import java.security.Key; +import java.util.regex.Pattern; public class JwtUtil { // 替换为实际的密钥,建议使用足够长的随机字符串 - private static final String SECRET_KEY = Processing.createJobNumber((short) 1, (short) 255); + private static final String SECRET_KEY = "238542310128901753637022851772455105464283332917211091531086967815273100806759714250034263888525489008903447113697698540563820710887668094087054975808574632265678643370464260078072153369247242449569221118098938297741582538222826493707667477115117609126233"; // Token 有效期,这里设置为一天,可以根据实际需求调整 private static final long EXPIRATION_TIME = 86400000; @@ -27,7 +28,7 @@ public class JwtUtil { } // 验证Token - public static boolean verify(String token, String username) { + public static boolean verify(String token) { try { Key key = Keys.hmacShaKeyFor(SECRET_KEY.getBytes()); @@ -38,9 +39,8 @@ public class JwtUtil { // 从JWT中获取用户名进行匹配 String tokenUsername = claimsJws.getBody().getSubject(); - // 验证用户名是否匹配 - return username.equals(tokenUsername); + return Pattern.matches("^[0-9A-Za-z_]{3,40}$", tokenUsername); } catch (Exception e) { // 验证失败 return false; diff --git a/src/main/java/com/jsl/oa/utils/Processing.java b/src/main/java/com/jsl/oa/utils/Processing.java index 74df2ea..a90dc8b 100644 --- a/src/main/java/com/jsl/oa/utils/Processing.java +++ b/src/main/java/com/jsl/oa/utils/Processing.java @@ -1,5 +1,6 @@ package com.jsl.oa.utils; +import org.jetbrains.annotations.NotNull; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; @@ -25,7 +26,7 @@ public class Processing { * @param bindingResult 参数校验结果 * @return {@link ArrayList} */ - public static ArrayList getValidatedErrorList(BindingResult bindingResult) { + public static @NotNull ArrayList getValidatedErrorList(BindingResult bindingResult) { ArrayList arrayList = new ArrayList<>(); for (ObjectError objectError : bindingResult.getAllErrors()) { arrayList.add(objectError.getDefaultMessage()); @@ -42,7 +43,7 @@ public class Processing { * @param type 0:学生 1:教师 2:其他 * @return {@link String} */ - public static String createJobNumber(Short type) { + public static @NotNull String createJobNumber(Short type) { return createJobNumber(type, (short) 10); } @@ -56,7 +57,7 @@ public class Processing { * @param size 工号长度 * @return {@link String} */ - public static String createJobNumber(Short type, Short size) { + public static @NotNull String createJobNumber(Short type, Short size) { StringBuilder stringBuilder = new StringBuilder(); if (type == 0) { stringBuilder.append("STU"); @@ -72,4 +73,17 @@ public class Processing { } return stringBuilder.toString(); } + + /** + * + */ + public static @NotNull String createCode() { + StringBuilder stringBuilder = new StringBuilder(); + // 生成验证码 + Random random = new Random(); + for (int i = 0; i < 6; i++) { + stringBuilder.append(random.nextInt(10)); + } + return stringBuilder.toString(); + } } diff --git a/src/main/resources/.gitkeep b/src/main/resources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 4f965f8..f200532 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,6 +8,8 @@ spring: database: 0 host: localhost port: 6379 + profiles: + active: dev mybatis: configuration: map-underscore-to-camel-case: true diff --git a/src/test/java/com/jsl/oa/JslOrganizeInternalOaApplicationTests.java b/src/test/java/com/jsl/oa/JslOrganizeInternalOaApplicationTests.java index 96601ad..23d7f62 100644 --- a/src/test/java/com/jsl/oa/JslOrganizeInternalOaApplicationTests.java +++ b/src/test/java/com/jsl/oa/JslOrganizeInternalOaApplicationTests.java @@ -10,7 +10,7 @@ class JslOrganizeInternalOaApplicationTests { @Test void contextLoads() { String token = JwtUtil.generateToken("admin"); - if (JwtUtil.verify(token, "admin")) { + if (JwtUtil.verify(token)) { System.out.println("验证通过"); } else { System.out.println("验证失败");