refactor: 优化登录策略;简化代码
This commit is contained in:
parent
27147998aa
commit
95593f6a5c
|
@ -1,20 +1,25 @@
|
|||
package cn.bunny.services.security.config;
|
||||
|
||||
import cn.bunny.dao.entity.system.AdminUser;
|
||||
import cn.bunny.dao.vo.result.ResultCodeEnum;
|
||||
import cn.bunny.services.mapper.UserMapper;
|
||||
import cn.bunny.services.security.custom.CustomAuthorizationManagerServiceImpl;
|
||||
import cn.bunny.services.security.custom.CustomPasswordEncoder;
|
||||
import cn.bunny.services.security.custom.service.CustomUserDetailsService;
|
||||
import cn.bunny.services.security.custom.service.impl.CustomAuthorizationManagerServiceImpl;
|
||||
import cn.bunny.services.security.filter.TokenLoginFilterService;
|
||||
import cn.bunny.services.security.handelr.SecurityAccessDeniedHandler;
|
||||
import cn.bunny.services.security.handelr.SecurityAuthenticationEntryPoint;
|
||||
import cn.bunny.services.service.UserService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
||||
|
@ -24,6 +29,7 @@ import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
|||
@EnableMethodSecurity
|
||||
public class WebSecurityConfig {
|
||||
|
||||
// 需要排出的无需验证的请求路径
|
||||
public static String[] annotations = {
|
||||
"/", "/ws/**",
|
||||
"/*/*/noAuth/**", "/*/noAuth/**", "/noAuth/**",
|
||||
|
@ -32,10 +38,7 @@ public class WebSecurityConfig {
|
|||
};
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private CustomUserDetailsService customUserDetailsService;
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private CustomPasswordEncoder customPasswordEncoder;
|
||||
|
@ -47,7 +50,7 @@ public class WebSecurityConfig {
|
|||
private AuthenticationConfiguration authenticationConfiguration;
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
public SecurityFilterChain filterChain(HttpSecurity httpSecurity, UserMapper userMapper) throws Exception {
|
||||
httpSecurity
|
||||
// 前端段分离不需要---禁用明文验证
|
||||
.httpBasic(AbstractHttpConfigurer::disable)
|
||||
|
@ -73,10 +76,35 @@ public class WebSecurityConfig {
|
|||
exception.accessDeniedHandler(new SecurityAccessDeniedHandler());
|
||||
})
|
||||
// 登录验证过滤器
|
||||
.addFilterBefore(new TokenLoginFilterService(authenticationConfiguration, customUserDetailsService), UsernamePasswordAuthenticationFilter.class)
|
||||
.addFilterBefore(new TokenLoginFilterService(authenticationConfiguration, userService), UsernamePasswordAuthenticationFilter.class)
|
||||
// 自定义密码加密器和用户登录
|
||||
.passwordManagement(customPasswordEncoder).userDetailsService(customUserDetailsService);
|
||||
.passwordManagement(customPasswordEncoder);
|
||||
|
||||
return httpSecurity.build();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 使用数据库方式
|
||||
* 登录方式:邮箱+用户名
|
||||
*
|
||||
* @param userMapper 获取用户数据
|
||||
* @return 数据库的用户
|
||||
*/
|
||||
@Bean
|
||||
public UserDetailsService userDetailsService(UserMapper userMapper) {
|
||||
return username -> {
|
||||
// 查询用户相关内容
|
||||
LambdaQueryWrapper<AdminUser> queryWrapper = new LambdaQueryWrapper<AdminUser>()
|
||||
.eq(AdminUser::getEmail, username)
|
||||
.or()
|
||||
.eq(AdminUser::getUsername, username);
|
||||
|
||||
// 根据邮箱查询用户名
|
||||
AdminUser adminUser = userMapper.selectOne(queryWrapper);
|
||||
if (adminUser == null) throw new UsernameNotFoundException(ResultCodeEnum.USER_IS_EMPTY.getMessage());
|
||||
|
||||
return adminUser;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,11 @@ import org.springframework.security.core.AuthenticationException;
|
|||
@ToString
|
||||
@Slf4j
|
||||
public class CustomAuthenticationException extends AuthenticationException {
|
||||
String message;
|
||||
ResultCodeEnum resultCodeEnum;
|
||||
|
||||
public CustomAuthenticationException(ResultCodeEnum codeEnum) {
|
||||
super(codeEnum.getMessage());
|
||||
this.resultCodeEnum = codeEnum;
|
||||
public CustomAuthenticationException(String message) {
|
||||
super(message);
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package cn.bunny.services.security.custom.service.impl;
|
||||
package cn.bunny.services.security.custom;
|
||||
|
||||
import cn.bunny.common.service.context.BaseContext;
|
||||
import cn.bunny.common.service.utils.JwtHelper;
|
||||
|
@ -9,7 +9,6 @@ import cn.bunny.dao.vo.result.ResultCodeEnum;
|
|||
import cn.bunny.dao.vo.system.user.LoginVo;
|
||||
import cn.bunny.services.mapper.PowerMapper;
|
||||
import cn.bunny.services.mapper.RoleMapper;
|
||||
import cn.bunny.services.security.custom.CustomAuthenticationException;
|
||||
import cn.bunny.services.utils.RoleUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
@ -19,6 +18,7 @@ import org.springframework.data.redis.core.RedisTemplate;
|
|||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
@ -54,12 +54,12 @@ public class CustomAuthorizationManagerServiceImpl implements AuthorizationManag
|
|||
// 判断是否有 token
|
||||
String token = request.getHeader("token");
|
||||
if (token == null) {
|
||||
throw new CustomAuthenticationException(ResultCodeEnum.LOGIN_AUTH);
|
||||
throw new UsernameNotFoundException(ResultCodeEnum.LOGIN_AUTH.getMessage());
|
||||
}
|
||||
|
||||
// 判断 token 是否过期
|
||||
if (JwtHelper.isExpired(token)) {
|
||||
throw new CustomAuthenticationException(ResultCodeEnum.AUTHENTICATION_EXPIRED);
|
||||
throw new UsernameNotFoundException(ResultCodeEnum.AUTHENTICATION_EXPIRED.getMessage());
|
||||
}
|
||||
|
||||
// 解析JWT中的用户名
|
||||
|
@ -72,12 +72,12 @@ public class CustomAuthorizationManagerServiceImpl implements AuthorizationManag
|
|||
|
||||
// 登录信息为空
|
||||
if (loginVo == null) {
|
||||
throw new CustomAuthenticationException(ResultCodeEnum.LOGIN_AUTH);
|
||||
throw new UsernameNotFoundException(ResultCodeEnum.LOGIN_AUTH.getMessage());
|
||||
}
|
||||
|
||||
// 判断用户是否禁用
|
||||
if (loginVo.getStatus()) {
|
||||
throw new CustomAuthenticationException(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED);
|
||||
throw new UsernameNotFoundException(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED.getMessage());
|
||||
}
|
||||
|
||||
// 设置用户信息
|
|
@ -1,24 +0,0 @@
|
|||
package cn.bunny.services.security.custom.service;
|
||||
|
||||
import cn.bunny.dao.dto.system.user.LoginDto;
|
||||
import cn.bunny.dao.vo.system.user.LoginVo;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
public interface CustomUserDetailsService extends UserDetailsService {
|
||||
/**
|
||||
* 根据用户名获取用户对象(获取不到直接抛异常)
|
||||
*/
|
||||
@Override
|
||||
UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
|
||||
|
||||
/**
|
||||
* 前台用户登录接口
|
||||
*
|
||||
* @param loginDto 登录参数
|
||||
* @return 登录后结果返回
|
||||
*/
|
||||
LoginVo login(LoginDto loginDto, HttpServletResponse response);
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
package cn.bunny.services.security.custom.service.impl;
|
||||
|
||||
import cn.bunny.common.service.exception.AuthCustomerException;
|
||||
import cn.bunny.dao.dto.system.user.LoginDto;
|
||||
import cn.bunny.dao.entity.system.AdminUser;
|
||||
import cn.bunny.dao.enums.LoginEnums;
|
||||
import cn.bunny.dao.vo.result.Result;
|
||||
import cn.bunny.dao.vo.result.ResultCodeEnum;
|
||||
import cn.bunny.dao.vo.system.user.LoginVo;
|
||||
import cn.bunny.services.mapper.UserMapper;
|
||||
import cn.bunny.services.security.custom.service.CustomUserDetailsService;
|
||||
import cn.bunny.services.utils.UserUtil;
|
||||
import cn.bunny.services.utils.login.DefaultLoginStrategy;
|
||||
import cn.bunny.services.utils.login.EmailLoginStrategy;
|
||||
import cn.bunny.services.utils.login.LoginContext;
|
||||
import cn.bunny.services.utils.login.LoginStrategy;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.DigestUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static cn.bunny.common.service.utils.ResponseUtil.out;
|
||||
|
||||
@Component
|
||||
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Autowired
|
||||
private UserUtil userUtil;
|
||||
|
||||
/**
|
||||
* 根据用户名获取用户对象(获取不到直接抛异常)
|
||||
*
|
||||
* @param username 用户名
|
||||
*/
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
// 查询用户相关内容
|
||||
LambdaQueryWrapper<AdminUser> queryWrapper = new LambdaQueryWrapper<AdminUser>()
|
||||
.eq(AdminUser::getEmail, username)
|
||||
.or()
|
||||
.eq(AdminUser::getUsername, username);
|
||||
|
||||
// 根据邮箱查询用户名
|
||||
AdminUser adminUser = userMapper.selectOne(queryWrapper);
|
||||
if (adminUser == null) throw new UsernameNotFoundException(ResultCodeEnum.USER_IS_EMPTY.getMessage());
|
||||
|
||||
// return new CustomUser(adminUser, AuthorityUtils.createAuthorityList());
|
||||
return adminUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 前台用户登录接口
|
||||
*
|
||||
* @param loginDto 登录参数
|
||||
* @return 登录后结果返回
|
||||
*/
|
||||
@Override
|
||||
public LoginVo login(LoginDto loginDto, HttpServletResponse response) {
|
||||
String password = loginDto.getPassword();
|
||||
Long readMeDay = loginDto.getReadMeDay();
|
||||
|
||||
// type不能为空
|
||||
String type = loginDto.getType();
|
||||
if (!StringUtils.hasText(type)) {
|
||||
out(response, Result.error(ResultCodeEnum.REQUEST_IS_EMPTY));
|
||||
return null;
|
||||
}
|
||||
|
||||
// 初始化登录策略,如果有需要添加策略放在这里
|
||||
HashMap<String, LoginStrategy> loginStrategyHashMap = new HashMap<>();
|
||||
loginStrategyHashMap.put(LoginEnums.EMAIL_STRATEGY.getValue(), new EmailLoginStrategy(redisTemplate, userMapper));
|
||||
loginStrategyHashMap.put(LoginEnums.default_STRATEGY.getValue(), new DefaultLoginStrategy(userMapper));
|
||||
|
||||
// 使用登录上下文调用登录策略
|
||||
LoginContext loginContext = new LoginContext(loginStrategyHashMap);
|
||||
AdminUser user = loginContext.executeStrategy(type, loginDto, response);
|
||||
|
||||
// 判断用户是否为空
|
||||
if (user == null) {
|
||||
out(response, Result.error(ResultCodeEnum.LOGIN_ERROR));
|
||||
return null;
|
||||
}
|
||||
|
||||
// 对登录密码进行md5加密判断,是否与数据库中一致
|
||||
String md5Password = DigestUtils.md5DigestAsHex(password.getBytes());
|
||||
if (!user.getPassword().equals(md5Password)) throw new AuthCustomerException(ResultCodeEnum.LOGIN_ERROR);
|
||||
|
||||
return userUtil.buildLoginUserVo(user, readMeDay);
|
||||
}
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
package cn.bunny.services.security.filter;
|
||||
|
||||
|
||||
import cn.bunny.common.service.utils.ResponseUtil;
|
||||
import cn.bunny.dao.dto.system.user.LoginDto;
|
||||
import cn.bunny.dao.enums.LoginEnums;
|
||||
import cn.bunny.dao.vo.result.Result;
|
||||
import cn.bunny.dao.vo.result.ResultCodeEnum;
|
||||
import cn.bunny.dao.vo.system.user.LoginVo;
|
||||
import cn.bunny.services.security.custom.service.CustomUserDetailsService;
|
||||
import cn.bunny.services.security.handelr.SecurityAuthenticationFailureHandler;
|
||||
import cn.bunny.services.security.handelr.SecurityAuthenticationSuccessHandler;
|
||||
import cn.bunny.services.service.UserService;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
@ -18,31 +18,27 @@ import org.springframework.security.authentication.UsernamePasswordAuthenticatio
|
|||
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static cn.bunny.common.service.utils.ResponseUtil.out;
|
||||
|
||||
/**
|
||||
* UsernamePasswordAuthenticationFilter
|
||||
* 也可以在这里添加验证码、短信等的验证
|
||||
* 由于SpringSecurity的登录只能是表单形式 并且用户名密码需要时username、password,可以通过继承 UsernamePasswordAuthenticationFilter 获取登录请求的参数
|
||||
* 再去设置到 UsernamePasswordAuthenticationToken 中 来改变请求传参方式、参数名等 或者也可以在登录的时候加入其他参数等等
|
||||
* 自定义登录
|
||||
* 自定义登录路径和自定义登录返回数据
|
||||
*/
|
||||
public class TokenLoginFilterService extends UsernamePasswordAuthenticationFilter {
|
||||
private final CustomUserDetailsService customUserDetailsService;
|
||||
private LoginDto loginDto;
|
||||
private final UserService userService;
|
||||
LoginDto loginDto;
|
||||
|
||||
public TokenLoginFilterService(AuthenticationConfiguration authenticationConfiguration, CustomUserDetailsService customUserDetailsService) throws Exception {
|
||||
public TokenLoginFilterService(AuthenticationConfiguration authenticationConfiguration, UserService customUserDetailsService) throws Exception {
|
||||
this.setAuthenticationSuccessHandler(new SecurityAuthenticationSuccessHandler());
|
||||
this.setAuthenticationFailureHandler(new SecurityAuthenticationFailureHandler());
|
||||
this.setPostOnly(false);
|
||||
// 自定义登录路径,自定义登录请求类型
|
||||
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(LoginEnums.LOGIN_REQUEST_API.getValue(), HttpMethod.POST.name()));
|
||||
this.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager());
|
||||
this.customUserDetailsService = customUserDetailsService;
|
||||
this.userService = customUserDetailsService;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,28 +52,20 @@ public class TokenLoginFilterService extends UsernamePasswordAuthenticationFilte
|
|||
loginDto = objectMapper.readValue(request.getInputStream(), LoginDto.class);
|
||||
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginDto.getUsername(), loginDto.getPassword());
|
||||
return getAuthenticationManager().authenticate(authentication);
|
||||
} catch (IOException e) {
|
||||
out(response, Result.error(ResultCodeEnum.ILLEGAL_DATA_REQUEST));
|
||||
return null;
|
||||
} catch (Exception exception) {
|
||||
throw new UsernameNotFoundException(exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证成功
|
||||
* 在这里用户的账号和密码是对的
|
||||
* 此时不要再去解析/转换 request.getInputStream() 因为流已经关闭
|
||||
*/
|
||||
@Override
|
||||
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication auth) {
|
||||
// 获取登录返回信息
|
||||
LoginVo loginVo = customUserDetailsService.login(loginDto, response);
|
||||
if (loginVo == null) return;
|
||||
|
||||
// 判断用户是否禁用
|
||||
if (loginVo.getStatus()) {
|
||||
out(response, Result.error(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED));
|
||||
return;
|
||||
}
|
||||
|
||||
out(response, Result.success(loginVo));
|
||||
LoginVo loginVo = userService.login(loginDto, response);
|
||||
ResponseUtil.out(response, Result.success(loginVo));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,12 +73,6 @@ public class TokenLoginFilterService extends UsernamePasswordAuthenticationFilte
|
|||
*/
|
||||
@Override
|
||||
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) {
|
||||
String password = loginDto.getPassword();
|
||||
String username = loginDto.getUsername();
|
||||
|
||||
if (!StringUtils.hasText(password) || !StringUtils.hasText(username))
|
||||
out(response, Result.error(ResultCodeEnum.USERNAME_OR_PASSWORD_NOT_EMPTY));
|
||||
else
|
||||
out(response, Result.error(null, ResultCodeEnum.LOGIN_ERROR));
|
||||
ResponseUtil.out(response, Result.error(null, failed.getMessage()));
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import cn.bunny.dao.vo.result.PageResult;
|
|||
import cn.bunny.dao.vo.system.user.*;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -20,6 +21,14 @@ import java.util.List;
|
|||
* @since 2024-09-26
|
||||
*/
|
||||
public interface UserService extends IService<AdminUser> {
|
||||
|
||||
/**
|
||||
* 前台用户登录接口
|
||||
*
|
||||
* @param loginDto 登录参数
|
||||
* @return 登录后结果返回
|
||||
*/
|
||||
LoginVo login(LoginDto loginDto, HttpServletResponse response);
|
||||
|
||||
/**
|
||||
* * 获取用户信息列表
|
||||
|
|
|
@ -13,6 +13,7 @@ import cn.bunny.dao.entity.system.AdminUser;
|
|||
import cn.bunny.dao.entity.system.Role;
|
||||
import cn.bunny.dao.entity.system.UserDept;
|
||||
import cn.bunny.dao.enums.EmailTemplateEnums;
|
||||
import cn.bunny.dao.enums.LoginEnums;
|
||||
import cn.bunny.dao.views.ViewUserDept;
|
||||
import cn.bunny.dao.vo.result.PageResult;
|
||||
import cn.bunny.dao.vo.result.ResultCodeEnum;
|
||||
|
@ -23,6 +24,10 @@ import cn.bunny.services.service.FilesService;
|
|||
import cn.bunny.services.service.UserService;
|
||||
import cn.bunny.services.utils.UserUtil;
|
||||
import cn.bunny.services.utils.email.ConcreteSenderEmailTemplate;
|
||||
import cn.bunny.services.utils.login.DefaultLoginStrategy;
|
||||
import cn.bunny.services.utils.login.EmailLoginStrategy;
|
||||
import cn.bunny.services.utils.login.LoginContext;
|
||||
import cn.bunny.services.utils.login.LoginStrategy;
|
||||
import cn.hutool.captcha.CaptchaUtil;
|
||||
import cn.hutool.captcha.CircleCaptcha;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
|
@ -30,12 +35,14 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.SneakyThrows;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
@ -84,6 +91,40 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, AdminUser> implemen
|
|||
@Autowired
|
||||
private RoleMapper roleMapper;
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
/**
|
||||
* 前台用户登录接口
|
||||
* 这里不用判断用户是否为空,因为在登录时已经校验过了
|
||||
* <p>
|
||||
* 抛出异常使用自带的 UsernameNotFoundException 或者自己封装<br/>
|
||||
* 但是这两个效果传入参数都是一样的,所以全部使用 UsernameNotFoundException
|
||||
* </p>
|
||||
*
|
||||
* @param loginDto 登录参数
|
||||
* @return 登录后结果返回
|
||||
*/
|
||||
@Override
|
||||
public LoginVo login(LoginDto loginDto, HttpServletResponse response) {
|
||||
Long readMeDay = loginDto.getReadMeDay();
|
||||
|
||||
// 初始化登录策略,如果有需要添加策略放在这里
|
||||
HashMap<String, LoginStrategy> loginStrategyHashMap = new HashMap<>();
|
||||
loginStrategyHashMap.put(LoginEnums.EMAIL_STRATEGY.getValue(), new EmailLoginStrategy(redisTemplate, userMapper));
|
||||
loginStrategyHashMap.put(LoginEnums.default_STRATEGY.getValue(), new DefaultLoginStrategy(userMapper));
|
||||
|
||||
// 使用登录上下文调用登录策略
|
||||
LoginContext loginContext = new LoginContext(loginStrategyHashMap);
|
||||
AdminUser user = loginContext.executeStrategy(loginDto);
|
||||
|
||||
// 判断用户是否禁用
|
||||
if (user.getStatus()) {
|
||||
throw new UsernameNotFoundException(ResultCodeEnum.FAIL_NO_ACCESS_DENIED_USER_LOCKED.getMessage());
|
||||
}
|
||||
return userUtil.buildLoginUserVo(user, readMeDay);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录发送邮件验证码
|
||||
*
|
||||
|
|
|
@ -4,7 +4,6 @@ import cn.bunny.dao.dto.system.user.LoginDto;
|
|||
import cn.bunny.dao.entity.system.AdminUser;
|
||||
import cn.bunny.services.mapper.UserMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 使用用户名登录
|
||||
|
@ -18,15 +17,15 @@ public class DefaultLoginStrategy implements LoginStrategy {
|
|||
|
||||
/**
|
||||
* 登录鉴定方法
|
||||
* 默认登录方式,使用用户名查询用户
|
||||
*
|
||||
* @param response 返回的响应
|
||||
* @param loginDto 登录参数
|
||||
* @return 鉴定身份验证
|
||||
*/
|
||||
@Override
|
||||
public AdminUser authenticate(HttpServletResponse response, LoginDto loginDto) {
|
||||
public AdminUser authenticate(LoginDto loginDto) {
|
||||
String username = loginDto.getUsername();
|
||||
String password = loginDto.getPassword();
|
||||
|
||||
// 查询用户相关内容
|
||||
LambdaQueryWrapper<AdminUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(AdminUser::getUsername, username);
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
package cn.bunny.services.utils.login;
|
||||
|
||||
import cn.bunny.common.service.utils.ResponseUtil;
|
||||
import cn.bunny.dao.constant.RedisUserConstant;
|
||||
import cn.bunny.dao.dto.system.user.LoginDto;
|
||||
import cn.bunny.dao.entity.system.AdminUser;
|
||||
import cn.bunny.dao.vo.result.Result;
|
||||
import cn.bunny.dao.vo.result.ResultCodeEnum;
|
||||
import cn.bunny.services.mapper.UserMapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
/**
|
||||
* 邮箱登录策略
|
||||
|
@ -25,26 +23,29 @@ public class EmailLoginStrategy implements LoginStrategy {
|
|||
|
||||
/**
|
||||
* 登录鉴定方法
|
||||
* 只要判断邮箱验证码是否相等,在前面已经验证过用户密码
|
||||
* <p>
|
||||
* 抛出异常类型 UsernameNotFoundException 如果要自定义状态码需要使用 HttpServletResponse
|
||||
* 有封装好的 ResponseUtil.out() 方法
|
||||
* </p>
|
||||
*
|
||||
* @param response 返回的响应
|
||||
* @param loginDto 登录参数
|
||||
* @return 鉴定身份验证
|
||||
*/
|
||||
@Override
|
||||
public AdminUser authenticate(HttpServletResponse response, LoginDto loginDto) {
|
||||
public AdminUser authenticate(LoginDto loginDto) {
|
||||
String username = loginDto.getUsername();
|
||||
String emailCode = loginDto.getEmailCode().toLowerCase();
|
||||
|
||||
// 查找Redis中的验证码
|
||||
Object redisEmailCode = redisTemplate.opsForValue().get(RedisUserConstant.getAdminUserEmailCodePrefix(username));
|
||||
if (redisEmailCode == null) {
|
||||
ResponseUtil.out(response, Result.error(ResultCodeEnum.EMAIL_CODE_EMPTY));
|
||||
return null;
|
||||
throw new UsernameNotFoundException(ResultCodeEnum.EMAIL_CODE_EMPTY.getMessage());
|
||||
}
|
||||
|
||||
// 判断用户邮箱验证码是否和Redis中发送的验证码
|
||||
if (!emailCode.equals(redisEmailCode.toString().toLowerCase())) {
|
||||
ResponseUtil.out(response, Result.error(ResultCodeEnum.EMAIL_CODE_NOT_MATCHING));
|
||||
return null;
|
||||
throw new UsernameNotFoundException(ResultCodeEnum.EMAIL_CODE_NOT_MATCHING.getMessage());
|
||||
}
|
||||
|
||||
// 查询用户相关内容
|
||||
|
|
|
@ -2,7 +2,7 @@ package cn.bunny.services.utils.login;
|
|||
|
||||
import cn.bunny.dao.dto.system.user.LoginDto;
|
||||
import cn.bunny.dao.entity.system.AdminUser;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -16,11 +16,19 @@ public class LoginContext {
|
|||
this.strategies = strategies;
|
||||
}
|
||||
|
||||
public AdminUser executeStrategy(String type, LoginDto loginDto, HttpServletResponse response) {
|
||||
/**
|
||||
* 执行登录策略
|
||||
* 根据情况判断 type 是否为空
|
||||
*
|
||||
* @param loginDto 登录参数
|
||||
* @return 用户
|
||||
*/
|
||||
public AdminUser executeStrategy(LoginDto loginDto) {
|
||||
String type = loginDto.getType();
|
||||
LoginStrategy strategy = strategies.get(type);
|
||||
if (strategy == null) {
|
||||
throw new IllegalArgumentException("不支持登录类型: " + type);
|
||||
throw new UsernameNotFoundException("不支持登录类型: " + type);
|
||||
}
|
||||
return strategy.authenticate(response, loginDto);
|
||||
return strategy.authenticate(loginDto);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ package cn.bunny.services.utils.login;
|
|||
|
||||
import cn.bunny.dao.dto.system.user.LoginDto;
|
||||
import cn.bunny.dao.entity.system.AdminUser;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 登录策略
|
||||
|
@ -12,9 +11,8 @@ public interface LoginStrategy {
|
|||
/**
|
||||
* 登录鉴定方法
|
||||
*
|
||||
* @param response 返回的响应
|
||||
* @param loginDto 登录参数
|
||||
* @return 鉴定身份验证
|
||||
*/
|
||||
AdminUser authenticate(HttpServletResponse response, LoginDto loginDto);
|
||||
AdminUser authenticate(LoginDto loginDto);
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ management:
|
|||
web:
|
||||
exposure:
|
||||
include: "*"
|
||||
base-path: /admin/actuator
|
||||
base-path: /api/actuator
|
||||
info:
|
||||
env:
|
||||
enabled: true
|
||||
|
|
Loading…
Reference in New Issue