一、SpringSecurity
SpringSecurity是一个强大且高效的安全框架,能够提供用户验证和访问控制服务,能够很好地整合到以Spring为基础的项目中。SpringBoot对SpringSecurity进行了大量的自动配置,使开发者通过少量的代码和配置就能完成很强大的验证和授权功能,下面我们就体验下SpringSecurity的基本使用。
引入依赖:引入spring security依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
springSecurity可以基于内存验证,也可以基于数据库作登录验证,在此就仅展示做数据库的验证
做登录验证,首先要有一个UserDetailServiceImpl来实现一个接口
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private IUserService userService;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//按用户名查询用户信息
User user = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getUsername, s));
if(user == null){
throw new UsernameNotFoundException("用户名不存在");
}
//通过用户名查询权限
List<String> list = userService.selectAuthoritiesByUsername(s);
//将集合转换为字符串,每个权限以逗号相隔
String s1=String.join(",",list);
//把用户信息包装到UserDetails的实现类User中
return new org.springframework.security.core.userdetails.User(user.getUsername(),user.getPassword(),
AuthorityUtils.commaSeparatedStringToAuthorityList(s1));
}
}
这个方法就是Security自带的登录验证功能,实现它就可以做登录验证
当然,还要做一些Security的配置工作,比如密码的加密算法啊,合成token字符串啊之类的,所以在做配置工作之前,还要自定义一些工具类,这里直接拿来用,复制粘贴即可
package com.blb.test.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.joda.time.DateTime;
import java.security.PrivateKey;
import java.security.PublicKey;
/**
* JWT工具类
*/
public class JwtUtils {
public static final String JWT_KEY_USERNAME = "username";
public static final int EXPIRE_MINUTES = 120;
/**
* 私钥加密token
*/
public static String generateToken(String username, PrivateKey privateKey, int expireMinutes) {
return Jwts.builder()
.claim(JWT_KEY_USERNAME, username)
.setExpiration(DateTime.now().plusMinutes(expireMinutes).toDate())
.signWith(SignatureAlgorithm.RS256, privateKey)
.compact();
}
/**
* 从token解析用户
*
* @param token
* @param publicKey
* @return
* @throws Exception
*/
public static String getUsernameFromToken(String token, PublicKey publicKey){
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
Claims body = claimsJws.getBody();
String username = (String) body.get(JWT_KEY_USERNAME);
return username;
}
}
主要是用来生成token字符串
还要有一个加密算法
package com.blb.test.util;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
/**
* RSA工具类
*/
public class RsaUtils {
public static final String RSA_SECRET = "blbweb@#$%"; //秘钥
public static final String RSA_PATH = System.getProperty("user.dir")+"/rsa/";//秘钥保存位置
public static final String RSA_PUB_KEY_PATH = RSA_PATH + "pubKey.rsa";//公钥路径
public static final String RSA_PRI_KEY_PATH = RSA_PATH + "priKey.rsa";//私钥路径
public static PublicKey publicKey; //公钥
public static PrivateKey privateKey; //私钥
/**
* 类加载后,生成公钥和私钥文件
*/
static {
try {
File rsa = new File(RSA_PATH);
if (!rsa.exists()) {
rsa.mkdirs();
}
File pubKey = new File(RSA_PUB_KEY_PATH);
File priKey = new File(RSA_PRI_KEY_PATH);
//判断公钥和私钥如果不存在就创建
if (!priKey.exists() || !pubKey.exists()) {
//创建公钥和私钥文件
RsaUtils.generateKey(RSA_PUB_KEY_PATH, RSA_PRI_KEY_PATH, RSA_SECRET);
}
//读取公钥和私钥内容
publicKey = RsaUtils.getPublicKey(RSA_PUB_KEY_PATH);
privateKey = RsaUtils.getPrivateKey(RSA_PRI_KEY_PATH);
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
/**
* 从文件中读取公钥
*
* @param filename 公钥保存路径,相对于classpath
* @return 公钥对象
* @throws Exception
*/
public static PublicKey getPublicKey(String filename) throws Exception {
byte[] bytes = readFile(filename);
return getPublicKey(bytes);
}
/**
* 从文件中读取密钥
*
* @param filename 私钥保存路径,相对于classpath
* @return 私钥对象
* @throws Exception
*/
public static PrivateKey getPrivateKey(String filename) throws Exception {
byte[] bytes = readFile(filename);
return getPrivateKey(bytes);
}
/**
* 获取公钥
*
* @param bytes 公钥的字节形式
* @return
* @throws Exception
*/
public static PublicKey getPublicKey(byte[] bytes) throws Exception {
X509EncodedKeySpec spec = new X509EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePublic(spec);
}
/**
* 获取密钥
*
* @param bytes 私钥的字节形式
* @return
* @throws Exception
*/
public static PrivateKey getPrivateKey(byte[] bytes) throws Exception {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(bytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return factory.generatePrivate(spec);
}
/**
* 根据密文,生存rsa公钥和私钥,并写入指定文件
*
* @param publicKeyFilename 公钥文件路径
* @param privateKeyFilename 私钥文件路径
* @param secret 生成密钥的密文
* @throws IOException
* @throws NoSuchAlgorithmException
*/
public static void generateKey(String publicKeyFilename, String privateKeyFilename, String secret) throws Exception {
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
SecureRandom secureRandom = new SecureRandom(secret.getBytes());
keyPairGenerator.initialize(1024, secureRandom);
KeyPair keyPair = keyPairGenerator.genKeyPair();
// 获取公钥并写出
byte[] publicKeyBytes = keyPair.getPublic().getEncoded();
writeFile(publicKeyFilename, publicKeyBytes);
// 获取私钥并写出
byte[] privateKeyBytes = keyPair.getPrivate().getEncoded();
writeFile(privateKeyFilename, privateKeyBytes);
}
private static byte[] readFile(String fileName) throws Exception {
return Files.readAllBytes(new File(fileName).toPath());
}
private static void writeFile(String destPath, byte[] bytes) throws IOException {
File dest = new File(destPath);
if (!dest.exists()) {
dest.createNewFile();
}
Files.write(dest.toPath(), bytes);
}
}
这里主要用RSA加密算法
当然,也可以自定义一些状态码,403啊500啊等,用AJAX流的方式来回调,也写在工具类中
package com.blb.test.util;
/**
* 响应状态枚举
*/
public enum ResponseStatus {
/**
* 内置状态
*/
OK(200,"操作成功"),
INTERNAL_ERROR(500000,"系统错误"),
BUSINESS_ERROR(500001,"业务错误"),
LOGIN_ERROR(500002,"账号或密码错误"),
NO_DATA_ERROR(500003,"没有找到数据"),
PARAM_ERROR(500004,"参数格式错误"),
AUTH_ERROR(401,"没有权限,需要登录");
//响应代码
private Integer code;
//响应消息
private String message;
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
ResponseStatus(Integer status, String message) {
this.code = status;
this.message = message;
}
}
package com.blb.test.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 响应数据封装对象
*/
@Data
public class ResponseResult<T> {
/**
* 状态信息
*/
private ResponseStatus status;
/**
* 数据
*/
private T data;
public ResponseResult(ResponseStatus status, T data) {
this.status = status;
this.data = data;
}
/**
* 返回成功对象
* @param data
* @return
*/
public static <T> ResponseResult<T> ok(T data){
return new ResponseResult<>(ResponseStatus.OK, data);
}
/**
* 返回错误对象
* @param status
* @return
*/
public static ResponseResult<String> error(ResponseStatus status){
return new ResponseResult<>(status,status.getMessage());
}
/**
* 返回错误对象
* @param status
* @return
*/
public static ResponseResult<String> error(ResponseStatus status,String msg){
return new ResponseResult<>(status,msg);
}
/**
* 向流中输出结果
* @param resp
* @param result
* @throws IOException
*/
public static void write(HttpServletResponse resp, ResponseResult result) throws IOException {
//设置返回数据的格式
resp.setContentType("application/json;charset=UTF-8");
//jackson是JSON解析包,ObjectMapper用于解析 writeValueAsString 将Java对象转换为JSON字符串
String msg = new ObjectMapper().writeValueAsString(result);
//用流发送给前端
resp.getWriter().print(msg);
resp.getWriter().close();
}
}
这样基本的准备工作就完成了,接下来就是Security的配置文件工作
//package com.blb.springsecurity.config;
//
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.context.annotation.Bean;
//import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
//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.configuration.WebSecurityConfigurerAdapter;
//import org.springframework.security.core.userdetails.UserDetailsService;
//import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
//import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
//
///**
// * SpringSecurity的核心配置
// */
启动Security的验证
//@EnableWebSecurity
//public class SecurityConfig extends WebSecurityConfigurerAdapter {
//
// //提供密码编码器
// @Bean
// public BCryptPasswordEncoder bCryptPasswordEncoder(){
// return new BCryptPasswordEncoder();
// }
//
// @Autowired
// private BCryptPasswordEncoder bCryptPasswordEncoder;
//
// @Autowired
// private UserDetailsService userDetailsService;
//
// @Autowired
// private PersistentTokenRepository persistentTokenRepository;
//
// //配置验证用户的账号和密码
// @Override
// protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中新建用户
auth.inMemoryAuthentication()
.withUser("zhangsan")
.password(bCryptPasswordEncoder.encode("123"))
.roles("ADMIN","USER") //角色
.and()
.withUser("lisi")
.password(bCryptPasswordEncoder.encode("123"))
.roles("USER"); //权限
// //数据库用户验证
// auth.userDetailsService(userDetailsService);
// }
//
// //配置访问控制
// @Override
// protected void configure(HttpSecurity http) throws Exception {
// //给请求授权
// http.authorizeRequests()
// //给登录相关的请求放行
// .antMatchers("/login","/login.html","/**/*.css").permitAll()
// //访问控制
// .antMatchers("/admin/**").hasAuthority("采购管理")
// .antMatchers("/user/**").hasAuthority("销售管理")
//
// //其余的都拦截
// .anyRequest().authenticated()
// .and()
// //配置自定义登录
// .formLogin()
// .loginPage("/login.html") //登录页面
// .loginProcessingUrl("/login") //处理登录的url
// .successForwardUrl("/hello.html") //登录成功后跳转的url
// ;
// http
// //配置记住我
// .rememberMe()
// //表单中的名称
// .rememberMeParameter("rememberMe")
// //jdbc操作对象
// .tokenRepository(persistentTokenRepository)
// //记住我的时间为60秒
// .tokenValiditySeconds(60);
// }
//}
package com.blb.test.config;
import com.blb.test.filter.RequestAuthenticationFilter;
import com.blb.test.util.ResponseResult;
import com.blb.test.util.ResponseStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
/**
* SpringSecurity的核心配置
*/
//启动权限控制的注解
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
//启动Security的验证
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//提供密码编码器
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private LoginSuccessHandler loginSuccessHandler;
//配置验证用户的账号和密码
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//数据库用户验证
auth.userDetailsService(userDetailsService);
}
//配置访问控制
@Override
protected void configure(HttpSecurity http) throws Exception {
//给请求授权
http.authorizeRequests()
//给登录相关的请求放行
.antMatchers("/login","/logout").permitAll()
//访问控制
//其余的都拦截
.anyRequest().authenticated()
.and()
//配置自定义登录
.formLogin()
.successHandler(loginSuccessHandler)//成功处理器
.failureHandler(((httpServletRequest, httpServletResponse, e) -> { //登录失败处理器
ResponseResult.write(httpServletResponse,ResponseResult.error(ResponseStatus.LOGIN_ERROR));
}))
.and()
.logout() //配置注销
.logoutSuccessHandler(((httpServletRequest, httpServletResponse, authentication) -> { //注销成功
ResponseResult.write(httpServletResponse,ResponseResult.ok(ResponseStatus.OK));
}))
.clearAuthentication(true) //清除验证信息
.and()
.cors() //配置跨域
.configurationSource(corsConfigurationSource())
.and()
.csrf().disable() //停止csrf
.sessionManagement() //session管理
.sessionCreationPolicy(SessionCreationPolicy.STATELESS) //无状态,不使用session
.and()
.addFilter(new RequestAuthenticationFilter(authenticationManager())) //添加自定义验证过滤器
;
}
/**
* 跨域配置对象
* @return
*/
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
//配置允许访问的服务器域名
configuration.setAllowedOrigins(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET","POST","PUT","DELETE"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
这里有一个问题就是我们都是将对象交给IOC容器来管理,在添加过滤的时候生成一个new RequestAuthenticationFilter 这个对象,这个对象是没有无参构造方法的,是不能自动注入交给IOC来管理的,因此我们要手动来生成对象来交给IOC管理,那么如何实现呢?记得刚接触Spring的时候吗?
有一个ApplicationContext,它继承了BeanFactory,所以知道了吧……因此,在工具类的包下建一个生成对象交给IOC管理的容器
package com.blb.test.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* 应用程序上下文工具
* 程序启动后,会创建ApplicationContext对象
* ApplicationContextAware能感知到ApplicationContext对象
* 自动调用setApplicationContext方法
*/
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
//系统的IOC容器
private static ApplicationContext applicationContext = null;
//感知到上下文后,自动调用,获得上下文
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtils.applicationContext = applicationContext;
}
//返回对象
public static <T> T getBean(Class<T> tClass){
return applicationContext.getBean(tClass);
}
}
还缺一个配置,大家想想是什么呢?没错,就是登录成功的处理器,登录成功该干啥呢?这个处理器就是来完成这个的配置的
package com.blb.test.config;
import com.blb.test.entity.vo.UserTokenVO;
import com.blb.test.util.JwtUtils;
import com.blb.test.util.ResponseResult;
import com.blb.test.util.RsaUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 登录成功处理器
*/
@Slf4j
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
//登录成功的回调
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
//获得用户名
User user = (User) authentication.getPrincipal();
//生成token字符串
String token = JwtUtils.generateToken(user.getUsername(), RsaUtils.privateKey, JwtUtils.EXPIRE_MINUTES);
log.info("生成token:{}",token);
//发送token给前端
ResponseResult.write(httpServletResponse,ResponseResult.ok(new UserTokenVO(user.getUsername(),token)));
}
}
这里仅仅是把token字符串回调给前端显示
Config配置完成后,大家想想接下来干嘛呢?那就是要做过滤的配置,就像之前做SpringWeb项目一样,登录要用过滤器进行过滤与放行,所以还要添加一个过滤器,Security的底层就是用各种过滤器来完成的,因此过滤器是十分重要的
package com.blb.test.filter;
import com.blb.test.service.IUserService;
import com.blb.test.util.ApplicationContextUtils;
import com.blb.test.util.JwtUtils;
import com.blb.test.util.RsaUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
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;
/**
* 请求验证过滤器
*/
@Slf4j
public class RequestAuthenticationFilter extends BasicAuthenticationFilter {
public static final String AUTH_HEADER = "Authorization";
//通过工具类获得service对象
private IUserService userService = ApplicationContextUtils.getBean(IUserService.class);
public RequestAuthenticationFilter(AuthenticationManager authenticationManager) {
super(authenticationManager);
}
//请求的过滤
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//从请求头获得token
String token = request.getHeader(AUTH_HEADER);
if(StringUtils.isEmpty(token)){
//从请求参数获得token
token = request.getParameter(AUTH_HEADER);
}
//如果读取不到,就拦截
if(StringUtils.isEmpty(token)){
log.info("读取不到token,请求{}被拦截",request.getRequestURL());
chain.doFilter(request,response);
return;
}
try {
//对token进行解析
String username = JwtUtils.getUsernameFromToken(token, RsaUtils.publicKey);
//将用户的权限查询出来
List<String> authList = userService.getAuthoritiesByUsername(username);
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", authList));
//创建通行证
UsernamePasswordAuthenticationToken authToken = new
UsernamePasswordAuthenticationToken(username,"",authorities);
//把通行证交给Security
SecurityContextHolder.getContext().setAuthentication(authToken);
}catch (Exception ex){
log.error("解析token失败",ex);
}
chain.doFilter(request,response);
}
}
这样基本的Security的登录验证就完成了,大家看看效果,用APIPost7
Authority
Security除了登录验证,还有一个核心就是权限,简单来说就是登录完成后有些页面的访问是需要权限的,比如管理员和普通用户的区别,这个很简单,只需要在Controller层的方法上加个注解即可,注解里写的是权限名称
@RestController
public class PermissionsController {
@Autowired
private IPermissionService permissionService;
@PreAuthorize("hasAuthority('系统功能')")
@RequestMapping("/permissions")
public List<Permission> selectAllPermission(){
List<Permission> permissions = permissionService.selectALlPermission();
return permissions;
}
这里的系统功能就是数据库管理员独有的权限,因此这个方法只能管理员来访问,如果没有相应的权限,那么就会报403的错误,当然,我们在访问的时候要在请求头上携带登录生成的token来确认你是哪个用户登录的,有没有这个权限
好了,Security就讲到这里,接下来是Redis
二、Redis
Redis(Remote Dictionary Server )远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存也可持久化的日志型、Key-Value(NoSQL)数据库。
Redis的特点
-
性能极高,基于内存,读的速度是110000次/s,写的速度是81000次/s
-
丰富的数据类型,支持string、hash、list、set及zset多种数据类型
-
原子性,所有操作都是原子性的,支持事务
-
丰富的特性,支持发布订阅、通知、过期策略等
-
支持持久化,可以将内存中的数据保存在磁盘中,重启后再次加载
-
支持分布式,理论上可以无限扩展
-
单线程,没有线程并发问题,Redis5.0后也支持多线程
应用场景:
1. 做为缓存,保存热点数据
2. 分布式锁、分布式ID、分布式Session
3. 消息队列
4. …
安装等步骤就不在此展示了,可以去看看其他教程安装,也很简单
Redis主要用来作缓存来使用,分为编程式缓存与声明式缓存,推荐使用声明式缓存,比较方便简单
声明式缓存的配置:
配置环境
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.database=0
spring.redis.jedis.pool.max-active=100
spring.redis.jedis.pool.max-wait=100ms
spring.redis.jedis.pool.max-idle=100
spring.redis.jedis.pool.min-idle=10
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
//启动缓存
@EnableCaching
@Configuration
public class RedisConfig {
//向IOC容器提供一个Redis操作对象,配置键和值的序列化
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
//创建Redis模板对象
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
//设置连接
template.setConnectionFactory(factory);
// 配置JSON序列化器
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置字符串序列化器
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson序列化器
template.setHashValueSerializer(jackson2JsonRedisSerializer);
// 完成配置
template.afterPropertiesSet();
return template;
}
声明式缓存的配置:
//配置声明式缓存
@Bean
public RedisCacheConfiguration provideRedisCacheConfiguration(){
//加载默认配置
RedisCacheConfiguration conf = RedisCacheConfiguration.defaultCacheConfig();
//返回Jackson序列化器
return conf.serializeValuesWith(
RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
}
使用步骤
缓存相关注解:
-
@CacheConfig 使用在Service类上,可以配置缓存名称,如:@CacheConfig(cacheNames = “books”)
-
@Cacheable 使用在查询方法上,让方法优先查询缓存
-
@CachePut 使用在更新和添加方法上,数据库更新和插入数据后同时保存到缓存里
-
@CacheEvict 使用在删除方法上,数据库删除后同时删除缓存
注意:缓存的实体类必须实现序列化接口
在ServiceImpl中方法上添加注解即可,如:
@Override
@Cacheable(cacheNames = "Permission",key = "T(String).valueOf(#id)")
public Permission selectById(Integer id) {
return permissionMapper.selectById(id);
}
这样就完成了Redis的声明式缓存,接下来看看效果,打开虚拟机,Linux中的Redis服务
运行服务后测试就可以看到缓存中的数据库
好了,今天的分享就到此结束,希望对大家有所帮助!