核心模块-security

This commit is contained in:
DB 2023-10-09 15:29:30 +08:00
parent 84d5e8fe13
commit 486dd07e43
27 changed files with 1611 additions and 7 deletions

View File

@ -38,6 +38,11 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- jwt 相关依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -57,6 +62,11 @@
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-compress -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -1,6 +1,5 @@
package com.cpop.core.aspect;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.cpop.common.constant.HttpStatus;
import com.cpop.common.enums.ErrorCodeEnum;
import com.cpop.core.base.R;
@ -11,6 +10,7 @@ import com.cpop.core.base.table.SysOperationLog;
import com.cpop.core.event.OperationLogEvent;
import com.cpop.core.utils.MessageUtils;
import com.cpop.core.utils.SecurityUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;

View File

@ -0,0 +1,35 @@
package com.cpop.core.base.entity;
import lombok.Data;
import java.io.Serializable;
/**
* @author DB
* @create 2023-04-05 17:53
*/
@Data
public class LoginForm implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 验证码
*/
private String code;
/**
* 单次标识
*/
private String userKey;
}

View File

@ -0,0 +1,34 @@
package com.cpop.core.base.entity;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Set;
/**
* @author: DB
* @date: 2022-09-26 22:48
* @Description: 登录成功vo
*/
@Data
@Accessors(chain = true)
public class LoginSuccess implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户id
*/
private String userId;
/**
* token
*/
private String token;
/**
* 权限
*/
private Set<String> role;
}

View File

@ -0,0 +1,47 @@
package com.cpop.core.base.entity.loginInfo;
import com.cpop.core.base.table.SysUser;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* @author DB
* @createTime 2023/09/10 13:02
* @description 系统员工登陆信息
*/
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
public class OamStaffLoginInfo extends SysUser {
/**
* 员工id(staffId)
*/
private String id;
/**
* 姓名
*/
private String name;
/**
* 部门id
*/
private String deptId;
/**
* 用户id
*/
private String userId;
/**
* 员工类型(0:技术人员;1:售后人员;2:管理人员)
*/
private Integer staffType;
/**
* 角色id
*/
private String roleId;
}

View File

@ -9,7 +9,14 @@ import lombok.Getter;
*/
@Getter
public enum OperationLogEnum {
/**
* 系统登录
*/
SYSTEM_LOGIN(0, "i18n_operationLog_systemLogin", "SystemLoginModel", 6, 4),
/**
* 系统用户退出登录
*/
SYSTEM_LOGOUT(1, "i18n_operationLog_systemLogout", "SystemLoginModel", 6, 4),
;
/**

View File

@ -0,0 +1,26 @@
package com.cpop.core.base.exception;
import org.springframework.security.core.AuthenticationException;
/**
* @author: DB
* @Date: 2023/08/24/18:10
* @Description: 自定义认证失败异常
*/
public class CpopAuthenticationException extends AuthenticationException {
private Object params;
public CpopAuthenticationException(String msg) {
super(msg);
}
public CpopAuthenticationException(String msg, Object params) {
super(msg);
this.params = params;
}
public Object getParams() {
return params;
}
}

View File

@ -47,7 +47,7 @@ public class CpopConfig {
this.version = version;
}
public static String getProfile() {
public String getProfile() {
return profile;
}
@ -66,28 +66,28 @@ public class CpopConfig {
/**
* 获取导入上传路径
*/
public static String getImportPath() {
public String getImportPath() {
return getProfile() + "/import";
}
/**
* 获取头像上传路径
*/
public static String getAvatarPath() {
public String getAvatarPath() {
return getProfile() + "/avatar";
}
/**
* 获取下载路径
*/
public static String getDownloadPath() {
public String getDownloadPath() {
return getProfile() + "/download/";
}
/**
* 获取上传路径
*/
public static String getUploadPath() {
public String getUploadPath() {
return getProfile() + "/upload";
}
}

View File

@ -0,0 +1,67 @@
package com.cpop.core.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author DB
* @Description: 生成密钥对的配置文件
* @create: 2023-08-04 23:06
*/
@Component
@ConfigurationProperties(prefix = "cpop.gateway.rsa-keypair")
public class GenerateKeyPairConfig {
/**
* 加密方式
*/
private String algorithm;
/**
* 初始化大小
*/
private Integer keySize;
/**
* 公钥文件
*/
private String publicKeyFile;
/**
* 私钥文件
*/
private String privateKeyFile;
/**
* 获取指定加密算法
* @return 读取YAML文件的 SystemConfig.rsa-keypair.algorithm 属性
*/
public String getAlgorithm() {
return algorithm;
}
/**
* 获取密钥长度用来初始化
* @return 读取YAML文件的 SystemConfig.rsa-keypair.key-size 属性
*/
public Integer getKeySize() {
return keySize;
}
/**
* 获取公钥存放文件
* @return 读取YAML文件的 SystemConfig.rsa-keypair.public-key-file 属性
*/
public String getPublicKeyFile() {
return publicKeyFile;
}
/**
* 获取私钥存放文件
* @return 读取YAML文件的 SystemConfig.rsa-keypair.private-key-file 属性
*/
public String getPrivateKeyFile() {
return privateKeyFile;
}
}

View File

@ -0,0 +1,19 @@
package com.cpop.core.config;
import com.mybatisflex.core.mybatis.FlexConfiguration;
import com.mybatisflex.spring.boot.ConfigurationCustomizer;
import org.springframework.context.annotation.Configuration;
/**
* @author DB
* @Description:
* @create 2023-08-27 12:13
*/
@Configuration
public class MybatisFlexConfig implements ConfigurationCustomizer {
@Override
public void customize(FlexConfiguration configuration) {
// 在这里为 configuration 进行配置
}
}

View File

@ -0,0 +1,225 @@
package com.cpop.core.config;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.StringUtils;
import com.cpop.core.filter.JwtAuthenticationFilter;
import com.cpop.core.filter.RepeatableFilter;
import com.cpop.core.gateway.miniProgram.MiniProgramAuthenticationFilter;
import com.cpop.core.gateway.miniProgram.MiniProgramAuthenticationProvider;
import com.cpop.core.gateway.oam.OamUsernamePasswordAuthenticationFilter;
import com.cpop.core.handler.*;
import com.cpop.core.service.impl.OamStaffDetailsServiceImpl;
import com.cpop.core.utils.JwtUtils;
import com.cpop.core.utils.PasswordEncoder;
import com.cpop.core.utils.SpringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
import java.util.Collections;
/**
* @author DB
* @Description: Security配置中心
* @since 2023-08-05 8:01
*/
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableMethodSecurity
public class SecurityConfig implements WebMvcConfigurer {
@Autowired
private JwtLogoutSuccessHandler jwtLogoutSuccessHandler;
@Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
@Autowired
private JwtAccessDeniedHandler jwtAccessDeniedHandler;
@Autowired
private RepeatableFilter repeatableFilter;
@Autowired
private LoginSuccessHandler loginSuccessHandler;
@Autowired
private LoginFailureHandler loginFailureHandler;
@Autowired
private CpopConfig cpopConfig;
@Autowired
private JwtUtils jwtUtils;
/**
* Security过滤拦截配置
*
* @param http 请求
* @return SecurityFilterChain 认证过滤链
* @throws Exception 异常
*/
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.cors(AbstractHttpConfigurer::disable)
.csrf(AbstractHttpConfigurer::disable)
//登录配置(多登陆模式自定义成功与失败->现已注释)
/*.formLogin(formLogin -> {
formLogin.successHandler(loginSuccessHandler)
.failureHandler(loginFailureHandler);
})*/
//登出配置
.logout(logout -> logout.logoutSuccessHandler(jwtLogoutSuccessHandler))
//禁用session
.sessionManagement(sessionManagement -> {
sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
})
//配置拦截规则
.authorizeHttpRequests(authorizeHttpRequests -> {
authorizeHttpRequests.antMatchers(StringUtils.split(jwtUtils.getWhiteList(), ",")).permitAll()
.anyRequest().authenticated();
})
//异常处理器
.exceptionHandling(exceptionHandling -> {
exceptionHandling.authenticationEntryPoint(jwtAuthenticationEntryPoint)
.accessDeniedHandler(jwtAccessDeniedHandler);
})
//自定义认证管理器
.authenticationManager(authenticationManager())
//流重复使用
.addFilterBefore(repeatableFilter, UsernamePasswordAuthenticationFilter.class)
// 添加 JWT 过滤器JWT 过滤器在退出认证过滤器之前
.addFilterBefore(authFilter(), LogoutFilter.class)
.addFilterAt(oamLoginFilter(http.getSharedObject(AuthenticationManager.class)), UsernamePasswordAuthenticationFilter.class)
//小程序认证
.addFilterAt(miniProgramAuthenticationFilter(http.getSharedObject(AuthenticationManager.class)), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
/**
* 自定义用户密码过滤器
*
* @param authenticationManager 认证管理器
* @return OamUsernamePasswordAuthenticationFilter oma管理器
*/
@Bean
public OamUsernamePasswordAuthenticationFilter oamLoginFilter(AuthenticationManager authenticationManager) {
OamUsernamePasswordAuthenticationFilter loginFilter = new OamUsernamePasswordAuthenticationFilter();
loginFilter.setFilterProcessesUrl("/login");
loginFilter.setAuthenticationManager(authenticationManager);
loginFilter.setAuthenticationSuccessHandler(loginSuccessHandler);
loginFilter.setAuthenticationFailureHandler(loginFailureHandler);
return loginFilter;
}
/**
* @Description: 默认的认证方式
* @return DaoAuthenticationProvider
* @author DB
* @Date: 2023/8/24 0024 16:37
*/
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(SpringUtils.getBean(OamStaffDetailsServiceImpl.class));
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
/**
* @Description: 小程序登陆认证方式(可自定义其他模式)
* @param authenticationManager 认证管理器
* @return MiniProgramAuthenticationFilter
* @author DB
* @Date: 2023/8/24 0024 10:43
*/
@Bean
public MiniProgramAuthenticationFilter miniProgramAuthenticationFilter(AuthenticationManager authenticationManager) {
MiniProgramAuthenticationFilter filter = new MiniProgramAuthenticationFilter();
filter.setAuthenticationManager(authenticationManager);
filter.setAuthenticationSuccessHandler(loginSuccessHandler);
filter.setAuthenticationFailureHandler(loginFailureHandler);
return filter;
}
@Bean
public MiniProgramAuthenticationProvider miniProgramAuthenticationProvider() {
return new MiniProgramAuthenticationProvider();
}
/**
* 获取AuthenticationManager认证管理器登录时认证使用
* @return AuthenticationManager
*/
@Bean
public AuthenticationManager authenticationManager(){
return new ProviderManager(Arrays.asList(authenticationProvider(), miniProgramAuthenticationProvider()));
}
/**
* 密码明文加密方式配置
* @return PasswordEncoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new PasswordEncoder();
}
/**
* jwt 校验过滤器 http 头部 Authorization 字段读取 token 并校验
* @return JwtAuthenticationFilter
*/
@Bean
public JwtAuthenticationFilter authFilter() {
return new JwtAuthenticationFilter();
}
/**
* 配置跨源访问(CORS)
* @return CorsConfigurationSource
*/
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.singletonList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
configuration.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type"));
configuration.applyPermitDefaultValues();
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
/**
* @param registry 资源注册
* @Description: 资源映射
* @Author DB
* @Date: 2023/5/27 13:57
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//本地文件上传路径
registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**")
.addResourceLocations("file:" + cpopConfig.getProfile() + "/");
}
}

View File

@ -0,0 +1,125 @@
package com.cpop.core.filter;
import com.alibaba.fastjson.JSONObject;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.ip.IpUtils;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.service.RedisService;
import com.cpop.core.utils.JwtUtils;
import com.pupu.core.security.miniProgram.MiniProgramAuthenticationToken;
import com.pupu.core.service.impl.OamStaffDetailsServiceImpl;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author: DB
* @date: 2022-09-25 16:34
* @Description: jwt认证过滤器
*/
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private OamStaffDetailsServiceImpl oamStaffDetailsService;
@Autowired
private RedisService redisService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String jwt = request.getHeader(jwtUtils.getHeader());
// 这里如果没有jwt继续往后走因为后面还有鉴权管理器等去判断是否拥有身份凭证所以是可以放行的
// 没有jwt相当于匿名访问若有一些接口是需要权限的则不能访问这些接口
if (StringUtils.isEmpty(jwt)) {
chain.doFilter(request, response);
return;
}
Jws<Claims> jws = jwtUtils.getClaimsByToken(jwt);
Claims claim = jws.getBody();
if (claim == null) {
throw new JwtException("token 异常");
}
if (jwtUtils.isTokenExpired(claim)) {
throw new JwtException("token 已过期");
}
String username = claim.getSubject();
UserType userType = UserType.valueOf(jws.getHeader().get(Constants.USER_TYPE).toString());
//从缓存中获取
JSONObject jsonObject = redisService.getCacheObject(userType.getKey() + username);
LoginUser loginUser;
if (null == jsonObject){
loginUser = multipleLoadUser(userType, username);
//存入缓存
redisService.setCacheObject(userType.getKey() + username, loginUser);
} else {
loginUser = jsonObject.toJavaObject(LoginUser.class);
}
//获取当前请求ip
loginUser.setIpAddr(IpUtils.getIpAddr(request));
multipleAuth(loginUser,username);
chain.doFilter(request, response);
}
/**
* @descriptions 多种获取用户信息
* @author DB
* @date 2023/09/11 13:49
* @param userType 用户类型
* @param username 用户名
* @return com.pupu.core.base.entity.LoginUser
*/
private LoginUser multipleLoadUser(UserType userType, String username) {
LoginUser loginUser = null;
switch (userType) {
//OAM
case OAM_USER:
// 获取用户的权限等信息
loginUser = (LoginUser) oamStaffDetailsService.loadUserByUsername(username);
break;
case MINI_USER:
break;
default:
}
return loginUser;
}
/**
* @Description: 多种认证方式
* @param loginUser 登陆用户信息
* @author DB
* @Date: 2023/8/24 0024 17:12
*/
private void multipleAuth(LoginUser loginUser, String username) {
switch (loginUser.getUserType()) {
case OAM_USER:
// 构建UsernamePasswordAuthenticationToken,这里密码为null是因为提供了正确的JWT,实现自动登录
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, null);
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
break;
case MINI_USER:
// MiniProgramAuthenticationToken,实现自动登录
MiniProgramAuthenticationToken miniProgramAuthenticationToken = new MiniProgramAuthenticationToken(username, loginUser);
SecurityContextHolder.getContext().setAuthentication(miniProgramAuthenticationToken);
break;
default:
}
}
}

View File

@ -0,0 +1,55 @@
package com.cpop.core.gateway.miniProgram;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* @author DB
* @Description: 小程序认证过滤器
* @create 2023-08-23 21:03
*/
public class MiniProgramAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public MiniProgramAuthenticationFilter() {
super(new AntPathRequestMatcher("/miniProgramLogin", "POST"));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!"POST".equals(request.getMethod())) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
ObjectMapper mapper = new ObjectMapper();
InputStream inputStream;
Map authenticationBean;
try {
inputStream = request.getInputStream();
authenticationBean = mapper.readValue(inputStream, Map.class);
} catch (IOException e) {
throw new RuntimeException(e);
}
String username = (String) authenticationBean.get("username");
String password = (String) authenticationBean.get("password");
username = username.trim();
MiniProgramAuthenticationToken authRequest = new MiniProgramAuthenticationToken(username, password);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
}
protected void setDetails(HttpServletRequest request, AbstractAuthenticationToken authRequest) {
authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
}
}

View File

@ -0,0 +1,38 @@
package com.cpop.core.gateway.miniProgram;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.enums.UserType;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
/**
* @author DB
* @Description: 自定义校验过程
* @create 2023-08-23 21:13
*/
public class MiniProgramAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
// 这个获取表单输入中返回的用户名;
String username = authentication.getName();
// 这个是表单中输入的密码
String password = (String)authentication.getCredentials();
//TODO:此处添加小程序认证失败返回 throw new RockBladeAuthenticationException("测试抛异常", UserType.MINI_USER);
//设置用户详情
LoginUser loginUser = new LoginUser();
loginUser.setUserName(username)
.setUserType(UserType.MINI_USER);
MiniProgramAuthenticationToken result = new MiniProgramAuthenticationToken(username, loginUser);
result.setDetails(loginUser);
return result;
}
@Override
public boolean supports(Class<?> authentication) {
//providerManager会遍历所有;securityConfig中注册的provider集合;根据此方法返回true或false来决定由哪个provider;去校验请求过来的authentication
return (MiniProgramAuthenticationToken.class.isAssignableFrom(authentication));
}
}

View File

@ -0,0 +1,72 @@
package com.cpop.core.gateway.miniProgram;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
/**
* @author DB
* @Description:
* @create 2023-08-23 21:11
*/
public class MiniProgramAuthenticationToken extends AbstractAuthenticationToken {
private final Object principal;
private Object credentials;
/**
* This constructor can be safely used by any code that wishes to create a
* <code>UsernamePasswordAuthenticationToken</code>, as the {@link #isAuthenticated()}
* will return <code>false</code>.
*
*/
public MiniProgramAuthenticationToken(Object principal, Object credentials) {
super(null);
this.principal = principal;
this.credentials = credentials;
setAuthenticated(false);
}
/**
* This constructor should only be used by <code>AuthenticationManager</code> or
* <code>AuthenticationProvider</code> implementations that are satisfied with
* producing a trusted (i.e. {@link #isAuthenticated()} = <code>true</code>)
* authentication token.
*
* @param principal
* @param credentials
* @param authorities
*/
public MiniProgramAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
// must use super, as we override
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
}
super.setAuthenticated(false);
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
credentials = null;
}
}

View File

@ -0,0 +1,45 @@
package com.cpop.core.gateway.oam;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* @author DB
* @create 2023-04-05 17:38
* OAM管理系统为基础认证系统,使用默认的过滤认证方式
*/
public class OamUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@SuppressWarnings("rawtypes")
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if ("POST".equalsIgnoreCase(request.getMethod())) {
ObjectMapper mapper = new ObjectMapper();
InputStream inputStream;
Map authenticationBean;
try {
inputStream = request.getInputStream();
authenticationBean = mapper.readValue(inputStream, Map.class);
String username = (String) authenticationBean.get("username");
String password = (String) authenticationBean.get("password");
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
this.setDetails(request, authRequest);
return this.getAuthenticationManager().authenticate(authRequest);
} catch (IOException e) {
throw new AuthenticationServiceException("Authentication method io exception: " + request.getMethod());
}
}
// 只能是post请求
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
}

View File

@ -0,0 +1,33 @@
package com.cpop.core.handler;
import com.alibaba.fastjson.JSONObject;
import com.cpop.core.base.R;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* @author: DB
* @date: 2022-09-25 16:55
* @Description: 无权限访问处理器
*/
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException {
httpServletResponse.setContentType("application/json;charset=UTF-8");
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
ServletOutputStream outputStream = httpServletResponse.getOutputStream();
R<String> result = R.fail(e.getMessage());
outputStream.write(JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}

View File

@ -0,0 +1,33 @@
package com.cpop.core.handler;
import com.alibaba.fastjson.JSONObject;
import com.cpop.core.base.R;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* @author: DB
* @date: 2022-09-25 16:40
* @Description: jwt认证失败处理器
*/
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
httpServletResponse.setContentType("application/json;charset=UTF-8");
httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ServletOutputStream outputStream = httpServletResponse.getOutputStream();
R<String> result = R.fail("请先登录");
outputStream.write(JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}

View File

@ -0,0 +1,77 @@
package com.cpop.core.handler;
import com.alibaba.fastjson.JSONObject;
import com.cpop.common.constant.Constants;
import com.cpop.core.base.R;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.enums.OperationLogEnum;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.service.CoreService;
import com.cpop.core.service.RedisService;
import com.cpop.core.utils.JwtUtils;
import com.cpop.core.utils.MessageUtils;
import com.cpop.core.utils.SpringUtils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* @author: DB
* @date: 2022-09-25 16:56
* @Description: 登出成功处理器
*/
@Component
public class JwtLogoutSuccessHandler implements LogoutSuccessHandler {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private RedisService redisService;
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException {
if (authentication != null) {
new SecurityContextLogoutHandler().logout(httpServletRequest, httpServletResponse, authentication);
}
String jwt = httpServletRequest.getHeader(jwtUtils.getHeader());
//添加退出登录成功日志
Jws<Claims> jws = jwtUtils.getClaimsByToken(jwt);
Claims claim = jws.getBody();
if (claim == null) {
throw new JwtException("token 异常");
}
if (jwtUtils.isTokenExpired(claim)) {
throw new JwtException("token 已过期");
}
String username = claim.getSubject();
UserType userType = UserType.valueOf(jws.getHeader().get(Constants.USER_TYPE).toString());
//从缓存中获取
JSONObject jsonObject = redisService.getCacheObject(userType.getKey() + username);
if (null != jsonObject) {
LoginUser loginUser = jsonObject.toJavaObject(LoginUser.class);
SpringUtils.getBean(CoreService.class).insertOperationLog(Constants.SUCCESS, OperationLogEnum.SYSTEM_LOGOUT, loginUser, MessageUtils.message("i18n_operationLog_systemLogout"));
}
//清除缓存
redisService.deleteObject(userType.getKey() + username);
httpServletResponse.setContentType("application/json;charset=UTF-8");
ServletOutputStream outputStream = httpServletResponse.getOutputStream();
String header = jwtUtils.getHeader();
httpServletResponse.setHeader(header, "");
R<String> result = R.ok(MessageUtils.message("i18n_loginOut_success"));
outputStream.write(JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}

View File

@ -0,0 +1,63 @@
package com.cpop.core.handler;
import com.alibaba.fastjson.JSONObject;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.ip.IpUtils;
import com.cpop.core.base.R;
import com.cpop.core.base.entity.LoginForm;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.enums.OperationLogEnum;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.base.exception.CpopAuthenticationException;
import com.cpop.core.service.CoreService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
/**
* @author: DB
* @date: 2022-09-25 16:24
* @Description: 登录失败handler
*/
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {
@Autowired
private CoreService coreService;
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
httpServletResponse.setContentType("application/json;charset=UTF-8");
ServletOutputStream outputStream = httpServletResponse.getOutputStream();
String jsonString = httpServletRequest.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
//映射登录表单
LoginForm loginFormBo = JSONObject.parseObject(jsonString, LoginForm.class);
//构建用户信息
LoginUser loginUser = new LoginUser();
loginUser.setUserName(loginFormBo.getUsername())
.setIpAddr(IpUtils.getIpAddr(httpServletRequest));
String errorMessage = "用户名或密码错误";
R<String> result;
//自定义认证异常,可以从这里获取到想要的参数
if(e instanceof CpopAuthenticationException) {
loginUser.setUserType((UserType) ((CpopAuthenticationException) e).getParams());
errorMessage = e.getMessage();
}
result = R.fail(errorMessage);
if (loginUser.getUserType() == UserType.OAM_USER) {
//添加登录失败日志
coreService.insertOperationLog(Constants.FAIL, OperationLogEnum.SYSTEM_LOGIN, loginUser, errorMessage);
}
outputStream.write(JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}

View File

@ -0,0 +1,90 @@
package com.cpop.core.handler;
import com.alibaba.fastjson.JSONObject;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.ip.IpUtils;
import com.cpop.core.base.R;
import com.cpop.core.base.entity.LoginSuccess;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.enums.OperationLogEnum;
import com.cpop.core.service.CoreService;
import com.cpop.core.service.RedisService;
import com.cpop.core.utils.JwtUtils;
import com.cpop.core.utils.MessageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* @author: DB
* @date: 2022-09-25 16:19
* @Description: 登录成功handler
*/
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Autowired
private JwtUtils jwtUtils;
@Autowired
private CoreService coreService;
@Autowired
private RedisService redisService;
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException{
httpServletResponse.setContentType("application/json;charset=UTF-8");
ServletOutputStream outputStream = httpServletResponse.getOutputStream();
LoginSuccess loginSuccessVo = new LoginSuccess();
LoginUser loginUser;
if (null != authentication.getPrincipal()) {
loginUser = (LoginUser) authentication.getPrincipal();
} else {
loginUser = (LoginUser) authentication.getDetails();
}
// 生成JWT并放置到请求头中
String jwt = jwtUtils.generateToken(authentication.getName(), loginUser.getUserType());
loginSuccessVo.setToken(jwt).setUserId(loginUser.getUserId()).setRole(loginUser.getPermissions());
String ipAddr = IpUtils.getIpAddr(httpServletRequest);
loginUser.setIpAddr(ipAddr);
//多种登陆方式
multipleLogin(loginUser);
R<LoginSuccess> result = R.ok(loginSuccessVo, MessageUtils.message("i18n_login_success"));
outputStream.write(JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
/**
* @Description: 多种登陆方式
* @param loginUser 登陆用户
* @author DB
* @Date: 2023/8/24 0024 17:12
*/
private void multipleLogin(LoginUser loginUser){
switch (loginUser.getUserType()) {
case OAM_USER:
//更新登录地址
coreService.updateSysUserLoginIp(loginUser.getIpAddr(), loginUser.getUsername());
//添加登录成功日志
coreService.insertOperationLog(Constants.SUCCESS, OperationLogEnum.SYSTEM_LOGIN, loginUser, MessageUtils.message("i18n_login_success"));
//将登录成功用户存入缓存
redisService.setCacheObject(loginUser.getUserType().getKey() + loginUser.getUsername(), loginUser);
break;
case MINI_USER:
//将登录成功用户存入缓存
redisService.setCacheObject(loginUser.getUserType().getKey() + loginUser.getUsername(), loginUser);
break;
default:
}
}
}

View File

@ -0,0 +1,39 @@
package com.cpop.core.service.impl;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.StringUtils;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.utils.SecurityUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.Set;
/**
* @author: DB
* @date: 2022-10-20 15:15
* @Description:
* AuthorityPermissionService自定义access表达式鉴权验证
* 可以将自定义的access添加到配置类中http.anyRequest.access(aps.hasPermission(xxx,xxx));
* 也可以直接使用注解@PreAuthorize@PreAuthorize(aps.hasPermission(xxx));
*/
@Service("aps")
public class AuthorityPermissionService {
public boolean hasPermission(String permissions) {
if (StringUtils.isEmpty(permissions)) {
return false;
}
// 用户信息对象
LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
if (loginUser == null || CollectionUtils.isEmpty(loginUser.getPermissions())) {
return false;
}
Set<String> authorities = loginUser.getPermissions();
return StringUtils.isNotEmpty(permissions) && hasPermissions(authorities, permissions);
}
private boolean hasPermissions(Set<String> authorities, String permission) {
return authorities.contains(Constants.ALL_PERMISSION) || authorities.contains(permission.trim());
}
}

View File

@ -0,0 +1,113 @@
package com.cpop.core.service.impl;
import com.cpop.common.constant.Constants;
import com.cpop.common.utils.bean.BeanUtils;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.entity.loginInfo.OamStaffLoginInfo;
import com.cpop.core.base.enums.UserType;
import com.cpop.core.base.table.SysUser;
import com.cpop.core.service.CoreService;
import com.cpop.core.utils.MessageUtils;
import com.mybatisflex.core.row.DbChain;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.row.RowUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author DB
* @createTime 2023/09/11 10:56
* @description oam员工登陆信息
*/
@Service("oamStaffDetailsService")
public class OamStaffDetailsServiceImpl implements UserDetailsService {
@Autowired
private CoreService coreService;
/**
* @descriptions 加载员工信息
* @author DB
* @date 2023/09/11 10:57
* @param username 用户名
* @return org.springframework.security.core.userdetails.UserDetails
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//统一获取系统用户信息
SysUser sysUser = coreService.getSysUser(username, UserType.OAM_USER);
if (sysUser == null) {
throw new UsernameNotFoundException(MessageUtils.message("i18n_alert_accountOrPwdError"));
}
//构建登陆员工信息
OamStaffLoginInfo staffLoginInfo = BeanUtils.mapToClass(sysUser, OamStaffLoginInfo.class);
staffLoginInfo.setUserId(sysUser.getId());
//员工
if (!staffLoginInfo.getUserName().equals(Constants.SUPER_ADMIN)) {
Row row = DbChain.table("pp_oam_staff")
.select()
.where("user_id = ?", staffLoginInfo.getUserId())
.one();
staffLoginInfo.setDeptId(row.getString("deptId"));
staffLoginInfo.setRoleId(row.getString("roleId"));
staffLoginInfo.setName(row.getString("name"));
staffLoginInfo.setStaffType(row.getInt("staffType"));
staffLoginInfo.setId(row.getString("id"));
}
//获取权限
Set<String> permissionSet = new HashSet<>();
if (Constants.SUPER_ADMIN.equals(username)) {
permissionSet.add(Constants.ALL_PERMISSION);
staffLoginInfo.setName(Constants.SUPER_ADMIN);
}
else {
//查询员工信息
List<Row> list = DbChain.table("oam_menu")
.select("pom.permission")
.from("pp_oam_menu").as("pom")
.leftJoin("pp_oam_role_menu").as("porm").on("porm.menu_id = pom.id")
.where("pom.type in (1,2)")
.and("porm.role_id = ?", staffLoginInfo.getRoleId())
.and("pom.permission is not null")
.list();
if (list.isEmpty()) {
permissionSet = new HashSet<>();
} else {
List<Permission> permissions = RowUtil.toEntityList(list, Permission.class);
permissionSet = permissions.stream().map(Permission::getPermission).collect(Collectors.toSet());
}
}
LoginUser loginUser = new LoginUser(sysUser.getId(), staffLoginInfo, permissionSet);
loginUser.setUserName(username)
.setLoginTime(System.currentTimeMillis())
.setStatus(sysUser.getStatus());
return loginUser;
}
/**
* 内部自用类
*/
public class Permission {
/**
* 权限
*/
private String permission;
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
}
}

View File

@ -0,0 +1,90 @@
package com.cpop.core.utils;
import com.cpop.common.constant.Constants;
import com.cpop.core.base.enums.UserType;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* @author: DB
* @date: 2022-09-25 16:14
* @Description: jwt核心工具类
*/
@Data
@Component
@ConfigurationProperties(prefix = "cpop.jwt")
public class JwtUtils {
/**
* 过期时间
*/
private Long expire;
/**
* 密钥
*/
private String secret;
/**
* 请求头
*/
private String header;
/**
* 白名单
*/
private String whiteList;
/**
* @author LOST.yuan
* @description 生成JWT
* @date 16:15 2022/9/25
* @param username 用户名
* @return {@link String}
**/
public String generateToken(String username, UserType userType) {
Date nowDate = new Date();
Date expireDate = new Date(nowDate.getTime() + 1000 * expire);
return Jwts.builder()
.setHeaderParam(Constants.USER_TYPE, userType)
.setSubject(username)
.setIssuedAt(nowDate)
// 7天过期
.setExpiration(expireDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/**
* @author LOST.yuan
* @description 解析JWT
* @date 16:15 2022/9/25
* @param jwt
* @return {@link Claims}
**/
public Jws<Claims> getClaimsByToken(String jwt) {
try {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(jwt);
} catch (Exception e) {
return null;
}
}
/**
* @author LOST.yuan
* @description 判断JWT是否过期
* @date 16:15 2022/9/25
* @param claims jwt令牌
* @return {@link boolean}
**/
public boolean isTokenExpired(Claims claims) {
return claims.getExpiration().before(new Date());
}
}

View File

@ -0,0 +1,38 @@
package com.cpop.core.utils;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @author: DB
* @date: 2022-09-25 17:01
* @Description:
*/
@NoArgsConstructor
public class PasswordEncoder extends BCryptPasswordEncoder {
@Autowired
private RsaUtils rsaUtils;
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
// 接收到的前端的密码
String pwd = rawPassword.toString();
//针对+变空格进行处理
pwd = pwd.replace(" ", "+");
// 进行rsa解密
try {
pwd = rsaUtils.decrypt(pwd);
} catch (Exception e) {
throw new BadCredentialsException(e.getMessage());
}
if (encodedPassword != null && encodedPassword.length() != 0) {
return BCrypt.checkpw(pwd, encodedPassword);
} else {
return false;
}
}
}

View File

@ -0,0 +1,203 @@
package com.cpop.core.utils;
import com.cpop.core.base.exception.UtilException;
import com.cpop.core.config.GenerateKeyPairConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
/**
* @author DB
* @Description: Rsa相关工具类
* @create 2023-08-04 23:09
*/
@Component
public class RsaUtils {
@Autowired
private GenerateKeyPairConfig keyPairConfig;
// region 私有方法
/**
* 生成密钥对
* @return 返回map集合其中包含publicKey与privateKey
* @throws NoSuchAlgorithmException
*/
private Map<String, Key> generateKeyPair() throws NoSuchAlgorithmException {
//RSA算法要求有一个可信任的随机数源
SecureRandom secureRandom = new SecureRandom();
//为RSA算法创建一个KeyPairGenerator对象
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(keyPairConfig.getAlgorithm());
//利用上面的随机数据源初始化这个KeyPairGenerator对象
keyPairGenerator.initialize(keyPairConfig.getKeySize(), secureRandom);
//生成密匙对
KeyPair keyPair = keyPairGenerator.generateKeyPair();
//得到公钥
Key publicKey = keyPair.getPublic();
//得到私钥
Key privateKey = keyPair.getPrivate();
Map<String, Key> keyPairMap = new HashMap<>();
keyPairMap.put("publicKey", publicKey);
keyPairMap.put("privateKey", privateKey);
return keyPairMap;
}
/**
* 获取文件中获取密钥对象
* @param fileName 文件名
* @return 密钥对象
*/
private Key getKeyFromFile(String fileName) {
Key key = null;
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(Files.newInputStream(Paths.get(fileName)));
key = (Key) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new UtilException(e.getMessage());
} finally {
try {
assert ois != null;
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return key;
}
/**
* 将密钥对生成到文件中
*/
private void generateKeyPairToFiles() {
ObjectOutputStream oosPublicKey = null;
ObjectOutputStream oosPrivateKey = null;
try {
Map<String, Key> keyPairMap = generateKeyPair();
Key publicKey = keyPairMap.get("publicKey");
Key privateKey = keyPairMap.get("privateKey");
oosPublicKey = new ObjectOutputStream(Files.newOutputStream(Paths.get(keyPairConfig.getPublicKeyFile())));
oosPrivateKey = new ObjectOutputStream(Files.newOutputStream(Paths.get(keyPairConfig.getPrivateKeyFile())));
oosPublicKey.writeObject(publicKey);
oosPrivateKey.writeObject(privateKey);
} catch (NoSuchAlgorithmException | IOException e) {
throw new UtilException(e.getMessage());
} finally {
try {
//清空缓存关闭文件输出流
assert oosPublicKey != null;
oosPublicKey.close();
assert oosPrivateKey != null;
oosPrivateKey.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 初始化密钥对文件
* @return 返回密钥对信息publicKey公钥字符串privateKey私钥字符串
*/
public Map<String, String> initKeyPair() {
Map<String, String> keyPairMap = new HashMap<>();
File publicFile = new File(keyPairConfig.getPublicKeyFile());
File privateFile = new File(keyPairConfig.getPrivateKeyFile());
//判断是否存在公钥和私钥文件
if (!publicFile.exists() || !privateFile.exists()) {
generateKeyPairToFiles();
}
ObjectInputStream oisPublic = null;
ObjectInputStream oisPrivate = null;
Key publicKey = null;
Key privateKey = null;
try {
oisPublic = new ObjectInputStream(Files.newInputStream(Paths.get(keyPairConfig.getPublicKeyFile())));
oisPrivate = new ObjectInputStream(Files.newInputStream(Paths.get(keyPairConfig.getPrivateKeyFile())));
publicKey = (Key) oisPublic.readObject();
privateKey = (Key) oisPrivate.readObject();
byte[] publicKeyBytes = publicKey.getEncoded();
byte[] privateKeyBytes = privateKey.getEncoded();
String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKeyBytes);
String privateKeyBase64 = Base64.getEncoder().encodeToString(privateKeyBytes);
//公钥字符串
keyPairMap.put("publicKey", publicKeyBase64);
//私钥字符串
keyPairMap.put("privateKey", privateKeyBase64);
} catch (IOException | ClassNotFoundException e) {
throw new UtilException(e.getMessage());
} finally {
try {
assert oisPrivate != null;
oisPrivate.close();
oisPublic.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return keyPairMap;
}
/**
* 加密方法
* @param source 源数据
* @return 加密后的字符串
*/
public String encrypt(String source) {
Key publicKey = getKeyFromFile(keyPairConfig.getPublicKeyFile());
Base64.Encoder encoder = Base64.getEncoder();
String encryptSource = null;
try {
//得到Cipher对象来实现对源数据的RSA加密
Cipher cipher = Cipher.getInstance(keyPairConfig.getAlgorithm());
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] bytes = source.getBytes();
//执行加密操作
encryptSource = encoder.encodeToString(cipher.doFinal(bytes));
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException |
BadPaddingException e) {
throw new UtilException(e.getMessage());
}
return encryptSource;
}
/**
* 解密方法
* @param source 密文
* @return 解密后的字符串
*/
public String decrypt(String source) {
Key privateKey = getKeyFromFile(keyPairConfig.getPrivateKeyFile());
Base64.Decoder decoder = Base64.getDecoder();
String decryptSource = null;
try {
//得到Cipher对象对已用公钥加密的数据进行RSA解密
Cipher cipher = Cipher.getInstance(keyPairConfig.getAlgorithm());
cipher.init(Cipher.DECRYPT_MODE, privateKey);
//执行解密操作
byte[] bytes = decoder.decode(source);
decryptSource = new String(cipher.doFinal(bytes), StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException |
IllegalBlockSizeException | BadPaddingException e) {
throw new UtilException(e.getMessage());
}
return decryptSource;
}
}

20
pom.xml
View File

@ -30,6 +30,8 @@
<fastjson.version>2.0.38</fastjson.version>
<mybatis-flex.version>1.6.4</mybatis-flex.version>
<easyexcel.version>3.3.2</easyexcel.version>
<commons-compress.version>1.21</commons-compress.version>
<jjwt.version>0.9.1</jjwt.version>
</properties>
<dependencyManagement>
@ -58,12 +60,24 @@
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- jwt 相关依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
<!--Cpop公共包-->
<dependency>
<groupId>com.cpop</groupId>
<artifactId>Cpop-Common</artifactId>
<version>${cpop.version}</version>
</dependency>
<!--Cpop公共包-->
<dependency>
<groupId>com.cpop</groupId>
<artifactId>Cpop-Core</artifactId>
<version>${cpop.version}</version>
</dependency>
<!--Mybatis-flex-->
<dependency>
<groupId>com.mybatis-flex</groupId>
@ -81,6 +95,12 @@
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-compress -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress.version}</version>
</dependency>
</dependencies>
</dependencyManagement>