当前位置: 首页 > news >正文

【SpringSecurity】基本开发流程

文章目录

    • 概要
    • 整体架构流程
    • 实现流程
      • 1、编写各种Handler
      • `2 、AccessToken处理器`
      • `3、定义AuthenticationFilter 继承 OncePerRequestFilter (OncePerRequestFilter是Spring提供的一个过滤器基类,它确保了在一次完整的HTTP请求中,无论请求经过多少次内部转发,过滤器的逻辑都只会被执行一次。这对于需要在请求处理之前或之后进行一次性设置或清理资源的场景特别有用。)`
      • `4、编写SecurityAutoConfiguration 把Handler和AccessTokenManager 注册成bean`
      • `5、编写 WebSecurityAutoConfiguration 注册SecurityFilterChain的bean`
      • `6 辅助类`
        • `6.1 公共配置注册(如何不在采用的地方用@EnableConfigurationProperties注解,可以写在公共配置文件里面)`
        • `6.2忽略url写在配置文件`
        • `6.3 自定义UserDetailsServiceImpl实现UserDetailsService`
        • `6.4 自定义redis操作`
        • `6.5 自定义加密解密(依据之前对接过微信支付相关token逻辑进行简单的RSA相关加解密)`
    • 小结

概要

security-spring-boot-starter
SpringSecurity参考笔记

整体架构流程

模块代码截图
在这里插入图片描述
表结构

实现流程

1、编写各种Handler

1.1 AccessDeniedHandler 处理当访问被拒绝时的逻辑

import com.core.web.Web;
import com.core.web.result.Result;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class myAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {Result<Object> result = Result.fail();if (accessDeniedException != null) {result.setMessage(accessDeniedException.getLocalizedMessage());}Web.Response.json(response, HttpStatus.FORBIDDEN, result);}
}

1.2 AuthenticationEntryPoint 处理未认证逻辑

import com.core.web.Web;
import com.core.web.result.Result;
import com.core.web.result.ResultCode;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class myAuthenticationEntryPoint implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {Result<Object> result = Result.build(ResultCode.UNAUTHORIZED);if (authException != null && !(authException instanceof InsufficientAuthenticationException)) {result.setMessage(authException.getLocalizedMessage());}Web.Response.json(response, HttpStatus.UNAUTHORIZED, result);}
}

1.3 AuthenticationFailureHandler 处理认证失败逻辑

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class myAuthenticationFailureHandler implements AuthenticationFailureHandler {@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)throws IOException, ServletException {Web.Response.json(response, Result.fail(exception.getLocalizedMessage()));}
}

1.4 AuthenticationSuccessHandler 处理认证成功逻辑

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class myAuthenticationSuccessHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)throws IOException, ServletException {LoginUser user = (LoginUser) authentication.getPrincipal();AccessTokenManager.create(user);Web.Response.json(response, Result.ok(user));}
}

1.5 LogoutSuccessHandler 处理退出登录逻辑
为了安全起见采用RES对token进行加密,后端拿到数据后进行解密再做进一步的判断

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;@Component
@EnableConfigurationProperties(TokenProperties.class) // 如何不写这个注解可以采用写在第五步的 CommonConfig
public class JsonLogoutSuccessHandler implements LogoutSuccessHandler {@Resourceprivate TokenProperties tokenProperties ;@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)throws IOException, ServletException {// 获取请求参数数据InputStream inputStream = request.getInputStream();//把inputStream 转mapObjectMapper mapper = new ObjectMapper();// 前端采用post 请求/logout,参数为key,value为token加密的密文的json数据Map<String,String> s = mapper.readValue(inputStream, Map.class);// 解密得到tokenString token = AesUtil.decryptToString(s.get("key"));// 获取登录用户信息LoginUser user = AccessTokenManager.getLoginUser(token);if($.isNotNull(user) &&  !StringUtils.equals(tokenProperties.getScreen(), token)){AccessTokenManager.remove(token);}Web.Response.json(response, Result.ok());}
}

在这里插入图片描述

2 、AccessToken处理器

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.InitializingBean;import static com.zyc.core.Constants.Separator.COLON;@RequiredArgsConstructor
public class AccessTokenManager implements InitializingBean {private static AccessTokenManager MANAGER;public static AccessTokenManager getInstance() {return MANAGER;}/*** 过期时间(单位:秒)*/private static final long EXPIRE_IN_SECONDS = 24 * 60 * 60;/*** 访问凭证缓存键*/public static final String TOKEN_CACHE_KEY = "token_to_user" + COLON;/*** 根据访问凭证获取登录用户信息** @param accessToken 访问凭证* @param <T>         用户信息类型* @return 登录用户信息*/public static <T extends LoginUser> T getLoginUser(String accessToken) {T user = RedisCache.get(getInstance().getCacheKey(accessToken));if (user == null) {return null;}refresh(accessToken, user);return user;}public static void remove(String accessToken) {RedisCache.del(getInstance().getCacheKey(accessToken));}public static String create(LoginUser user) {String token = $.id();user.setToken(token);refresh(token, user);return token;}public static void refresh(String accessToken, LoginUser user) {if ($.isBlank(accessToken)) {return;}boolean isSuperAdmin = SecureContextHolder.hasSuperAdmin(user.getPermissions());if (isSuperAdmin) {RedisCache.set(getInstance().getCacheKey(accessToken), user);} else {RedisCache.set(getInstance().getCacheKey(accessToken), user, EXPIRE_IN_SECONDS);}}String getCacheKey(String accessToken) {return TOKEN_CACHE_KEY.concat(accessToken);}@Overridepublic void afterPropertiesSet() throws Exception {MANAGER = this;}
}

3、定义AuthenticationFilter 继承 OncePerRequestFilter (OncePerRequestFilter是Spring提供的一个过滤器基类,它确保了在一次完整的HTTP请求中,无论请求经过多少次内部转发,过滤器的逻辑都只会被执行一次。这对于需要在请求处理之前或之后进行一次性设置或清理资源的场景特别有用。)

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
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;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/*** 认证过滤* <p>* 用于从请求信息 {@link HttpServletRequest} 获取访问凭证,获取登录信息,设置到安全认证上下文中;* 以实现登录* </p>* <p>* set before at {@link org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter}* </p>**/
@Slf4j
public class AuthenticationFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {log.debug("[auth.filter] server_name: {} uri: {}, query: {}",request.getServerName(), request.getRequestURI(), request.getQueryString());Authentication authentication = getAuthentication(request);SecurityContextHolder.getContext().setAuthentication(authentication);Optional.ofNullable(authentication).ifPresent(auth -> {List<String> authorities = auth.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());ContextHolder.set(SUPER_ADMIN, SecureContextHolder.hasSuperAdmin(authorities));});//TODO 对参数进行一些过滤校验/*ParameterRequestWrapper paramRequest = new ParameterRequestWrapper(request);chain.doFilter(paramRequest, response);*/chain.doFilter(request, response);}/*** 获取认证信息** @param request 请求信息* @return 认证信息*/private Authentication getAuthentication(HttpServletRequest request) {String accessToken = obtainAccessToken(request);log.debug("[auth.filter] obtain access_token: {}", accessToken);if ($.isBlank(accessToken)) {return null;}LoginUser login = AccessTokenManager.getLoginUser(accessToken);return Optional.ofNullable(login).map(user -> new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities())).orElse(null);}/*** 获取访问凭证** @param request 请求信息* @return 访问凭证*/private String obtainAccessToken(HttpServletRequest request) {String token = request.getHeader(TOKEN_HEAD);return Optional.ofNullable(token).orElseGet(() -> request.getParameter(TOKEN_PARAM));}
}

4、编写SecurityAutoConfiguration 把Handler和AccessTokenManager 注册成bean

import com.security.servlet.AccessTokenManager;
import com.security.servlet.handler.*;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;@EnableConfigurationProperties(SecurityProperties.class) // 如何不写这个注解可以采用写在第五步的 CommonConfig
@Configuration(proxyBeanMethods = false)
@Import(WebSecurityAutoConfiguration.class)
public class SecurityAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic AccessTokenManager accessTokenManager() {return new AccessTokenManager();}@Bean@ConditionalOnMissingBeanpublic AuthenticationEntryPoint authenticationEntryPoint() {return new JsonAuthenticationEntryPoint();}@Bean@ConditionalOnMissingBeanpublic AccessDeniedHandler accessDeniedHandler() {return new JsonAccessDeniedHandler();}@Bean@ConditionalOnMissingBeanpublic AuthenticationFailureHandler authenticationFailureHandler() {return new JsonAuthenticationFailureHandler();}@Bean@ConditionalOnMissingBeanpublic AuthenticationSuccessHandler authenticationSuccessHandler() {return new JsonAuthenticationSuccessHandler();}@Bean@ConditionalOnMissingBeanpublic PasswordEncoder passwordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}@Bean@ConditionalOnMissingBeanpublic LogoutSuccessHandler logoutSuccessHandler() {return new JsonLogoutSuccessHandler();}
}

5、编写 WebSecurityAutoConfiguration 注册SecurityFilterChain的bean

import com.security.configurers.AuthorizeRequestsCustomizer;
import com.security.servlet.filter.AuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET
)
@Configuration(proxyBeanMethods = false)
@RequiredArgsConstructor
public class WebSecurityAutoConfiguration {/*** 默认静态资源常量数组*/private static final String[] DEFAULT_STATIC_RESOURCES = new String[]{"/favicon.ico","/**/*.css","/**/*.js","/doc.html","/swagger-ui/**","/swagger-ui.html","/swagger-ui/index.html","/swagger-resources/**","/v3/api-docs","/v3/api-docs/**"};private final SecurityProperties securityProperties;private final AuthenticationSuccessHandler authenticationSuccessHandler;private final AuthenticationFailureHandler authenticationFailureHandler;private final AuthenticationEntryPoint authenticationEntryPoint;private final AccessDeniedHandler accessDeniedHandler;private final LogoutSuccessHandler loggingSuccessHandler;private final List<AuthorizeRequestsCustomizer> authorizeRequestsCustomizers;@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.formLogin(form -> form.successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler))// CSRF 禁用,因为不使用 Session.csrf(AbstractHttpConfigurer::disable).sessionManagement(sessionManagement ->sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS))// 异常处理.exceptionHandling(exceptionHandling ->exceptionHandling.accessDeniedHandler(accessDeniedHandler).authenticationEntryPoint(authenticationEntryPoint))// 忽略认证.authorizeRequests(auth -> auth.antMatchers(DEFAULT_STATIC_RESOURCES).permitAll().antMatchers($.toArray(securityProperties.getIgnoreUrls())).permitAll())// 兜底,必须通过认证才能访问.authorizeRequests().anyRequest().authenticated().and()// 前置通过凭证获取登录信息.addFilterBefore(new AuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)//退出逻辑.logout().logoutSuccessHandler(loggingSuccessHandler);return http.build();}AntPathRequestMatcher[] convertRequestMatcher(Stream<String> stream) {return stream.map(AntPathRequestMatcher::new).collect(Collectors.toList()).toArray(new AntPathRequestMatcher[]{});}
}

6 辅助类

6.1 公共配置注册(如何不在采用的地方用@EnableConfigurationProperties注解,可以写在公共配置文件里面)
import com..autoconfigure.ConfigProperties;
import com.boot.autoconfigure.security.TokenProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;@EnableConfigurationProperties({SecurityProperties.class,TokenProperties.class
})
@Configuration
public class CommonConfig {
}
6.2忽略url写在配置文件
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.ArrayList;
import java.util.List;@Data
@ConfigurationProperties("cfg.security")
public class SecurityProperties {/*** 忽略 url*/private List<String> ignoreUrls = new ArrayList<>();
}

特定token配置在配置文件

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@Data
@ConfigurationProperties(prefix = "ems.token")
public class TokenProperties {private String screen;
}

自定义LoginUser 继承UserDetails

import cn.hutool.core.map.MapUtil;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;import java.time.LocalDate;
import java.util.Collection;
import java.util.Map;
import java.util.Set;@Data
@Accessors(chain = true)
public class LoginUser implements UserDetails, CredentialsContainer {/*** 主键标识*/private String id;/*** 用户名*/private String username;/*** 密码*/private String password;/*** 昵称*/private String nickname;/*** 权限集合*/private Set<String> permissions;/*** 访问凭证*/private String token;/*** 扩展信息*/private Map<String, Object> extra;/*** 上下文信息*/private Map<String, Object> context;/*** 机构标识*/private String orgId;/*** 机构类型*/private Integer orgType;/*** 机构名称*/private String orgName;/*** 有效期*/private LocalDate validityPeriod;@JsonIgnore@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return AuthorityUtils.createAuthorityList($.toArray(permissions, String.class));}@JsonIgnore@Overridepublic String getPassword() {return password;}@JsonIgnore@Overridepublic boolean isAccountNonExpired() {return true;}@JsonIgnore@Overridepublic boolean isAccountNonLocked() {return true;}@JsonIgnore@Overridepublic boolean isCredentialsNonExpired() {return true;}@JsonIgnore@Overridepublic boolean isEnabled() {return true;}@Overridepublic void eraseCredentials() {this.password = null;}
}
6.3 自定义UserDetailsServiceImpl实现UserDetailsService
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AccountExpiredException;
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.time.LocalDate;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {private final OperatorService operatorService;private final RoleService roleService;private final MenuService menuService;private final ProjectService projectService;private final OrgService orgService;@Overridepublic UserDetails loadUserByUsername(String username)throws UsernameNotFoundException {Operator operator = operatorService.lambdaQuery().eq(Operator::getUsername, username).oneOpt().orElseThrow(() -> new UsernameNotFoundException("用户名或者密码错误"));List<Role> roles = roleService.listByOperator(operator.getId(), operator.getOrgId());boolean isSuperAdmin = roles.stream().anyMatch(role -> $.equals(role.getId(), Constants.Default.SUPER_ADMIN));Set<String> roleIds = roles.stream().map(Role::getId).collect(Collectors.toSet());// 菜单权限Set<String> permissions = menuService.listByRole(isSuperAdmin, $.toArray(roleIds, String.class)).stream().map(Menu::getPermission).filter(Kit::isNotBlank).collect(Collectors.toSet());permissions.addAll(roleIds.stream().map(Constants.Default.ROLE_PREFIX::concat).collect(Collectors.toList()));// 项目权限Set<String> projectIds = projectService.listByRole(operator.getOrgId(), isSuperAdmin, roles);permissions.addAll(projectIds.stream().filter(Kit::isNotBlank).map(Values.PROJECT_PREFIX::concat).collect(Collectors.toList()));String password = Constants.Security.DEFAULT_ENCRYPTION_SIGNATURE.concat(operator.getPassword());Org org = orgService.getById(operator.getOrgId());//如果机构设置了有效期,则有效期过后不能登录LocalDate now = LocalDate.now();if ($.isNotNull(org.getValidityPeriod())) {if (now.isAfter(org.getValidityPeriod())) {throw new AccountExpiredException("账户过期,请联系管理员");}}LoginUser user = new LoginUser().setUsername(username).setPassword(password).setPermissions(permissions);user.setId(operator.getId());user.setNickname(operator.getNickname());user.setOrgId(operator.getOrgId());user.setOrgType(org.getOrgType());user.setOrgName(org.getOrgName());user.setValidityPeriod(org.getValidityPeriod());return user;}}
6.4 自定义redis操作
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.*;import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;@SuppressWarnings("unchecked")
@Slf4j
public class RedisCache {private static RedisCache redisCache;private final RedisTemplate<String, Object> redisTemplate;private final ValueOperations<String, Object> valueOperations;private final HashOperations<String, String, Object> hashOperations;private final ListOperations<String, Object> listOperations;private final SetOperations<String, Object> setOperations;private final ZSetOperations<String, Object> zSetOperations;private final String keyPrefix;public RedisCache(RedisTemplate<String, Object> redisTemplate, String keyPrefix) {this.redisTemplate = redisTemplate;this.keyPrefix = keyPrefix;this.valueOperations = redisTemplate.opsForValue();this.hashOperations = redisTemplate.opsForHash();this.listOperations = redisTemplate.opsForList();this.setOperations = redisTemplate.opsForSet();this.zSetOperations = redisTemplate.opsForZSet();redisCache = this;}/*** 获取缓存** @param key 键* @param <T> 类型* @return 指定类型值*/public static <T> T get(String key) {return key == null ? null : (T) opsValue().get(obtainKey(key));}/*** 写入缓存** @param key   键* @param value 值* @return true:成功*/public static boolean set(String key, Object value) {try {opsValue().set(obtainKey(key), value);return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** 写入缓存并设置过期时间** @param key    键* @param value  值* @param second 过期时间(单位:秒)* @return true:成功*/public static boolean set(String key, Object value, long second) {try {if (second > 0) {opsValue().set(obtainKey(key), value, second, TimeUnit.SECONDS);} else {set(obtainKey(key), value);}return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** 写入缓存并设置过期时间** @param key   键* @param value 值* @param time  过期时间* @param unit  时间单位* @return true:成功*/public static boolean set(String key, Object value, long time, TimeUnit unit) {try {if (time > 0) {opsValue().set(obtainKey(key), value, time, unit);} else {set(obtainKey(key), value);}return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** 设置指定缓存的失效时间** @param key    键* @param second 过期时间(单位:秒)* @return true:成功*/public static boolean expire(String key, long second) {try {if (second > 0) {template().expire(obtainKey(key), second, TimeUnit.SECONDS);}return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** 获取指定缓存的过期时间** @param key 键* @return 过期时间;0:代表永久有效*/public static long getExpire(String key) {return Optional.ofNullable(template().getExpire(obtainKey(key), TimeUnit.SECONDS)).orElse(0L);}/*** 判断缓存是否存在** @param key 键* @return true:存在*/public static boolean has(String key) {try {return Optional.ofNullable(template().hasKey(obtainKey(key))).orElse(false);} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** 删除缓存** @param key 键(支持多个)*/public static void del(String... key) {if (key != null && key.length > 0) {if (key.length == 1) {template().delete(obtainKey(key[0]));} else {template().delete(Arrays.stream(key).map(RedisCache::obtainKey).collect(Collectors.toList()));}}}/*** 递增指定数值** @param key   键* @param delta 递增数值(大于0)* @return 递增后的数值*/public static long incr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递增数值必须大于 0");}return Optional.ofNullable(opsValue().increment(obtainKey(key), delta)).orElse(0L);}/*** 递减指定数值** @param key   键* @param delta 递减数值(大于0)* @return 递减后的数值*/public static long decr(String key, long delta) {if (delta < 0) {throw new RuntimeException("递减数值必须大于 0");}return Optional.ofNullable(opsValue().decrement(obtainKey(key), delta)).orElse(0L);}/*** Hash* 获取指定字段的值** @param key   键* @param field 字段* @param <T>   类型* @return 指定类型值*/public static <T> T hget(String key, String field) {return (T) opsHash().get(obtainKey(key), field);}/*** Hash* 设置指定键字段值** @param key   键* @param field 字段* @param value 值* @return true:成功*/public static boolean hset(String key, String field, Object value) {try {opsHash().put(obtainKey(key), field, value);return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** Hash* 设置指定键字段值** @param key    键* @param field  字段* @param value  值* @param second 过期时间(单位:秒)* @return true:成功*/public static boolean hset(String key, String field, Object value, long second) {try {opsHash().put(obtainKey(key), field, value);if (second > 0) {expire(obtainKey(key), second);}return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** Hash* 获取所有的键值** @param key 键* @return 所有的键值*/public static <T> Map<String, T> hgetall(String key) {return (Map<String, T>) opsHash().entries(obtainKey(key));}/*** Hash* 设置多个字段值** @param key 键* @param map 多个字段值* @return true:成功*/public static <T> boolean hmset(String key, Map<String, T> map) {try {opsHash().putAll(obtainKey(key), map);return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** Hash* 设置多个字段值并设置过期时间** @param key    键* @param map    多个字段值* @param second 过期时间(单位:秒)* @return true:成功*/public static <T> boolean hmset(String key, Map<String, T> map, long second) {try {opsHash().putAll(obtainKey(key), map);if (second > 0) {expire(obtainKey(key), second);}return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** Hash* 删除指定键指定字段缓存** @param key    键* @param fields 字段(支持多个)*/public static void hdel(String key, Object... fields) {opsHash().delete(obtainKey(key), fields);}/*** Hash* 判断指定字段是否存在** @param key   键* @param field 字段* @return true:成功*/public static boolean hhas(String key, String field) {return opsHash().hasKey(obtainKey(key), field);}/*** Hash* 递增指定字段数值** @param key   键* @param field 字段* @param delta 递增数值* @return 递增后的数值*/public static long hincr(String key, String field, long delta) {return opsHash().increment(obtainKey(key), field, delta);}/*** Hash* 递减指定字段数值** @param key   键* @param field 字段* @param delta 递减数值* @return 递减后的数值*/public static long hdecr(String key, String field, long delta) {return opsHash().increment(obtainKey(key), field, -delta);}/*** Set* 添加元素** @param key    键* @param values 值(支持多个)* @return 成功个数*/public static long sadd(String key, Object... values) {try {return Optional.ofNullable(opsSet().add(obtainKey(key), values)).orElse(0L);} catch (Exception e) {log.error(e.getMessage(), e);return 0L;}}/*** Set* 添加元素** @param key    键* @param second 过期时间(单位:秒)* @param values 值(支持多个)* @return 成功个数*/public static long sadd(String key, long second, Object... values) {try {Long count = opsSet().add(obtainKey(key), values);if (second > 0) {expire(obtainKey(key), second);}return Optional.ofNullable(count).orElse(0L);} catch (Exception e) {log.error(e.getMessage(), e);return 0;}}/*** Set* 获取缓存大小** @param key 键* @return 大小*/public static long scard(String key) {try {return Optional.ofNullable(opsSet().size(obtainKey(key))).orElse(0L);} catch (Exception e) {log.error(e.getMessage(), e);return 0;}}/*** Set* 移除元素** @param key    键* @param values 值(支持多个)* @return 移除个数*/public static long srem(String key, Object... values) {try {return Optional.ofNullable(opsSet().remove(obtainKey(key), values)).orElse(0L);} catch (Exception e) {log.error(e.getMessage(), e);return 0;}}/*** List* 列表头部插入缓存** @param key   键* @param value 值* @return true:成功*/public static boolean lpush(String key, Object value) {try {opsList().leftPush(obtainKey(key), value);return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** List* 列表头部插入多个缓存** @param key    键* @param second 过期时间(单位秒)* @param values 值(支持多个)* @return true:成功*/public static boolean lpush(String key, long second, Object... values) {try {opsList().leftPushAll(obtainKey(key), values);if (second > 0) {expire(obtainKey(key), second);}return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** List* 列表尾部插入缓存** @param key   键* @param value 值* @return true:成功*/public static boolean lrpush(String key, Object value) {try {opsList().rightPush(obtainKey(key), value);return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** List* 列表尾部插入多个缓存** @param key    键* @param second 过期时间(单位秒)* @param values 值(支持多个)* @return true:成功*/public static boolean lrpush(String key, long second, Object... values) {try {opsList().rightPushAll(obtainKey(key), values);if (second > 0) {expire(obtainKey(key), second);}return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** List* 设置指定索引的值** @param key   键* @param index 索引* @param value 值* @return true:成功*/public static boolean lset(String key, long index, Object value) {try {opsList().set(obtainKey(key), index, value);return true;} catch (Exception e) {log.error(e.getMessage(), e);return false;}}/*** List* 获取指定范围内的元素** @param key   键* @param start 开始索引* @param end   结束索引* @param <T>   类型* @return 元素列表*/public static <T> List<T> lrange(String key, long start, long end) {try {return (List<T>) opsList().range(obtainKey(key), start, end);} catch (Exception e) {log.error(e.getMessage(), e);return null;}}/*** List* 获取指定列表缓存的长度** @param key 键* @return 长度*/public static long llen(String key) {try {return Optional.ofNullable(opsList().size(obtainKey(key))).orElse(0L);} catch (Exception e) {log.error(e.getMessage(), e);return 0;}}/*** List* 获取指定索引对应的值** @param key   键* @param index 索引* @param <T>   类型* @return 值*/public static <T> T lindex(String key, long index) {try {return (T) opsList().index(obtainKey(key), index);} catch (Exception e) {log.error(e.getMessage(), e);return null;}}/*** List* 移动指定个数的元素** @param key   键* @param count 移除个数* @param value 值* @return 成功移除个数*/public static long lrem(String key, long count, Object value) {try {return Optional.ofNullable(opsList().remove(obtainKey(key), count, value)).orElse(0L);} catch (Exception e) {log.error(e.getMessage(), e);return 0;}}private static String obtainKey(String key) {return Optional.ofNullable(redisCache.keyPrefix).map(prefix -> prefix + key).orElse(key);}public static RedisTemplate<String, Object> template() {return redisCache.redisTemplate;}public static ValueOperations<String, Object> opsValue() {return redisCache.valueOperations;}public static HashOperations<String, String, Object> opsHash() {return redisCache.hashOperations;}public static ListOperations<String, Object> opsList() {return redisCache.listOperations;}public static SetOperations<String, Object> opsSet() {return redisCache.setOperations;}@SuppressWarnings({"AlibabaLowerCamelCaseVariableNaming"})public static ZSetOperations<String, Object> opsZSet() {return redisCache.zSetOperations;}public static GeoOperations<String, Object> opsGeo() {return template().opsForGeo();}public static StreamOperations<String, Object, Object> opsStream() {return template().opsForStream();}public static ClusterOperations<String, Object> opsCluster() {return template().opsForCluster();}
}
6.5 自定义加密解密(依据之前对接过微信支付相关token逻辑进行简单的RSA相关加解密)
package com.zyc.security.servlet.handler;//import com.ahlinksoft.common.utils.SysPropUtils;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Decoder;import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@Component
@Slf4j
public class AesUtil {static final int KEY_LENGTH_BYTE = 32;static final int TAG_LENGTH_BIT = 128;private final static String publicKey ="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC69/jlQYoDKIWLR9dGD0/LLInD+KZyptdrdeqPQY9lBRmgcR2DHvyEDOe6cbGvV+0HcfOef6FFEqW2dzadjglbBqPMQttb5+0kEn4bZT+j2mMvQex51tD7AgmbEvV5XFi9DBYtxaM8c3DhBI+gobF3zzybqj3T9qY6ZPzAaQmKywIDAQAB";private final static String privateKey ="MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBALr3+OVBigMohYtH10YPT8ssicP4pnKm12t16o9Bj2UFGaBxHYMe/IQM57pxsa9X7Qdx855/oUUSpbZ3Np2OCVsGo8xC21vn7SQSfhtlP6PaYy9B7HnW0PsCCZsS9XlcWL0MFi3FozxzcOEEj6ChsXfPPJuqPdP2pjpk/MBpCYrLAgMBAAECgYBFQlhz7FxIoTkfCCTJsiBX3g7GhpR6Ai3og8y49os826/LjDtneBIIhJNOsJEmqAaI7IBXZWYDJU43kl+r65Kw/z0h81jCFNOxyBmSkweWRXGYl77BxmTtxvvDrEEcj3z2e/plzeOwJpncErqwPgXz60zpuXmkNvtm5YnK+jIp0QJBAPaGU4ZpQjWbdjE9fSEkXYI3tReNpsGLuGbaExw4P5v1iaSuSIwhz0oorRMDWvXijAEEkDKYG+yV8B8s9w/F8okCQQDCJ6VAe3MBln/wCxMI2ksnEDfViFLzHljzrw4QWuq4pFkM0LmjqBAOh4SuGtnSAATg+Dn3cBcrW61lXY7d0A2zAkAH2q++kIvzeUiCuRrLe4UfKPc0EOdHM1Rksn35YDFmnqQMAyF8lXctPa6F9I1pWBZt0d9mPUJmf0BvPdeBQSVBAkAUg/XtO14qo8l/BtyLCe35Abi0ooQJVoKVtUCqL8Evo4YZWZDUHzUbI+y8LPApRYE+eFw/riN0nGpVFK6893gLAkA0VQEWHjzeq0qCJL8F9UXW67zfT/J3hMlahK0YXxfdU9kusqMjvHxzLg6+iZX/jaK1T3kWj2EafwUSxhJf1b7X";private static Map<String, String> params = new HashMap<>();//私钥解密public static byte[] decrypt(byte[] content, PrivateKey privateKey) throws Exception{Cipher cipher=Cipher.getInstance("RSA");cipher.init(Cipher.DECRYPT_MODE, privateKey);return cipher.doFinal(content);}//私钥解密public static String decryptToString(String ciphertext) {try {Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.DECRYPT_MODE, getPrivateKey(privateKey));byte[ ] content =Base64.getDecoder().decode(ciphertext);byte[ ] doFinalResult = null;try {doFinalResult = cipher.doFinal(content);} catch (IllegalBlockSizeException e) {throw new RuntimeException(e);} catch (BadPaddingException e) {throw new RuntimeException(e);}try {return new String(doFinalResult, "utf-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {throw new IllegalStateException(e);} catch (InvalidKeyException e) {throw new IllegalArgumentException(e);}}//将base64编码后的公钥字符串转成PublicKey实例public static PublicKey getPublicKey(String publicKey) throws Exception{byte[ ] keyBytes=Base64.getDecoder().decode(publicKey.getBytes());X509EncodedKeySpec keySpec=new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory=KeyFactory.getInstance("RSA");return keyFactory.generatePublic(keySpec);}/*** String转私钥PrivateKey* @param key* @return* @throws Exception*/public static PrivateKey getPrivateKey(String key) {byte[] keyBytes;try {keyBytes = (new BASE64Decoder()).decodeBuffer(key);} catch (IOException e) {throw new RuntimeException(e);}PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = null;try {keyFactory = KeyFactory.getInstance("RSA");} catch (NoSuchAlgorithmException e) {throw new RuntimeException(e);}PrivateKey privateKey = null;try {privateKey = keyFactory.generatePrivate(keySpec);} catch (InvalidKeySpecException e) {throw new RuntimeException(e);}return privateKey;}//生成密钥对public static KeyPair genKeyPair(int keyLength) throws Exception{KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance("RSA");keyPairGenerator.initialize(1024);return keyPairGenerator.generateKeyPair();}/*** 获取私钥。** @param filename 私钥文件路径  (required)* @return 私钥对象*//*public static PrivateKey getPrivateKey(String filename) throws IOException {String content = new String(Files.readAllBytes(Paths.get(filename)), "utf-8");try {String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");KeyFactory kf = KeyFactory.getInstance("RSA");return kf.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));} catch (NoSuchAlgorithmException e) {throw new RuntimeException("当前Java环境不支持RSA", e);} catch (InvalidKeySpecException e) {throw new RuntimeException("无效的密钥格式");}}*/public static void main(String[] args) throws Exception {String data="028fbb4b16bf48bf93a02d1d0687e149";/*KeyPair keyPair=genKeyPair(1024);//获取公钥,并以base64格式打印出来PublicKey publicKey=keyPair.getPublic();System.out.println("公钥:"+new String(Base64.getEncoder().encode(publicKey.getEncoded())));//获取私钥,并以base64格式打印出来PrivateKey privateKey=keyPair.getPrivate();System.out.println("私钥:"+new String(Base64.getEncoder().encode(privateKey.getEncoded())));*///公钥加密byte[] encryptedBytes=encrypt(data.getBytes(), getPublicKey(publicKey));log.debug("加密后:{}",encryptToString(encryptedBytes));//私钥解密byte[] decryptedBytes=decrypt(encryptedBytes, getPrivateKey(privateKey));log.debug("解密后:{}",new String(decryptedBytes,"utf-8"));//私钥解密String  decryptedString = decryptToString(encryptToString(encryptedBytes));log.debug("解密后:{}",decryptedString);}//公钥加密public static byte[] encrypt(byte[] content, PublicKey publicKey) throws Exception{Cipher cipher=Cipher.getInstance("RSA");//java默认"RSA"="RSA/ECB/PKCS1Padding"cipher.init(Cipher.ENCRYPT_MODE, publicKey);return cipher.doFinal(content);}public static String encryptToString(byte[] encryptedBytes) throws Exception{return Base64.getEncoder().encodeToString(encryptedBytes);}}

小结

提示:这里可以添加总结

http://www.lryc.cn/news/526412.html

相关文章:

  • Redis实战(黑马点评)——关于缓存(缓存更新策略、缓存穿透、缓存雪崩、缓存击穿、Redis工具)
  • ChatGPT从数据分析到内容写作建议相关的46个提示词分享!
  • 在 Windows 11 中设置 WSL2 Ubuntu 的 `networkingMode=mirrored` 详细教程
  • 万字长文总结前端开发知识---JavaScriptVue3Axios
  • 怎么样把pdf转成图片模式(不能复制文字)
  • 本地centos网络配置
  • kotlin内联函数——runCatching
  • Python3 正则表达式:文本处理的魔法工具
  • 《DiffIR:用于图像修复的高效扩散模型》学习笔记
  • windows平台通过命令行安装前端开发环境
  • 记交叉编译asio_dtls过程
  • 学习yosys(一款开源综合器)
  • 自定义数据集 使用tensorflow框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测
  • 对于Docker的初步了解
  • C语言进阶——3字符函数和字符串函数(2)
  • 机器学习day3
  • 追剧记单词之:国色芳华与单词速记
  • AIGC浪潮下,图文内容社区数据指标体系构建探索
  • 总线、UART、IIC、SPI
  • 戴尔电脑设置u盘启动_戴尔电脑设置u盘启动多种方法
  • 【python】四帧差法实现运动目标检测
  • JVM学习指南(48)-JVM即时编译
  • office 2019 关闭word窗口后卡死未响应
  • [操作系统] 深入进程地址空间
  • CVE-2025-0411 7-zip 漏洞复现
  • leetcode151-反转字符串中的单词
  • 若依 v-hasPermi 自定义指令失效场景
  • vue3中自定一个组件并且能够用v-model对自定义组件进行数据的双向绑定
  • 使用 Python 和 Tesseract 实现验证码识别
  • 谈一谈前端构建工具的本地代理配置(Webpack与Vite)