用户微服务代码改造
改造成微服务架构后,一般会在网关层进行鉴权操作,所以这部分代码会进行一些调整。不过,改造的只是代码层面和实现细节,其底层的原理并没有太多改动,依然可以根据笔者提供的登录流程来理解和学习。
本节的源代码是在 newbee-mall-cloud-dev-step03
工程的基础上改造的,将工程命名为 newbee-mall-cloud-dev-step04
。改造思路是将原来存储在商城管理员 token
表中的数据改为存储到 Redis
中,这样在网关层也可以通过读取 Redis
中的数据实现用户鉴权操作。
引入Redis进行鉴权改造
引入与 Redis
相关的依赖并配置好相关的连接参数。
第一步,引入依赖。
打开 newbee-mall-cloud-user-web
模块下的 pom.xml
文件,在 dependencies
标签下引入与 Redis
相关的依赖,新增配置代码如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
第二步,新增连接 Redis
数据库的配置项。
在 newbee-mall-cloud-user-web
模块下的 application.properties
配置文件中增加连接 Redis
的配置项,代码如下:
# Redis 配置
# Redis 数据库索引(默认为 0)
spring.redis.database=13
# Redis 服务器地址
spring.redis.host=127.0.0.1
# Redis 服务器连接端口
spring.redis.port=6379
# Redis 服务器连接密码
spring.redis.password=123456
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000
连接 Redis
的配置项主要包括 Redis
服务器地址、数据库索引、账号密码、连接超时时间等。
第三步,新增 Redis
配置类。
在 newbee-mall-cloud-user-web
模块下的 ltd.user.cloud.newbee.config
包中新增自定义的 Redis
配置类,代码如下:
package ltd.user.cloud.newbee.config;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import serializer.StringRedisSerializer;
import javax.annotation.Resource;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
@Configuration
@EnableCaching
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig extends CachingConfigurerSupport {
@Resource
private LettuceConnectionFactory lettuceConnectionFactory;
public RedisConfig() {
}
@Bean
public RedisTemplate<String, Serializable> redisCacheTemplate
(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
public CacheManager cacheManager() {
RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(lettuceConnectionFactory);
@SuppressWarnings("serial")
Set<String> cacheNames = new HashSet<String>() {
{
add("codeNameCache");
}
};
builder.initialCacheNames(cacheNames);
return builder.build();
}
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(target.getClass().getName());
stringBuffer.append(method.getName());
for (Object obj : params) {
stringBuffer.append(obj.toString());
}
return stringBuffer.toString();
}
};
}
}
以上代码主要配置 RedisTemplate
对象,并自定义一些序列化方式。以上代码是 Redis
配置类常见的配置方式,可以直接在代码中使用。
另外,在项目开发中使用 Redis
时,一般会自定义一个静态工具类,把一些常见的缓存操作方法抽取出来方便调用。在本项目中对缓存的操作并不复杂,因此并未单独抽取 Redis
工具类,而是直接使用 RedisTemplate
类中提供的底层方法。
用户微服务中登录代码及鉴权代码修改
原来的登录逻辑中会生成 AdminUserToken
的相关数据并保存到 MySQL
数据库中,现在引入了 Redis
数据库,把这部分数据保存到 Redis
数据库中即可,修改后的登录方法代码如下:
@Autowired
private RedisTemplate redisTemplate;
@Override
public String login(String userName, String password) {
AdminUser loginAdminUser = adminUserMapper.login(userName, password);
if (loginAdminUser != null) {
//登录后执行修改 token 值的操作
String token = getNewToken(System.currentTimeMillis() + "",
loginAdminUser.getAdminUserId());
AdminUserToken adminUserToken = new AdminUserToken();
adminUserToken.setAdminUserId(loginAdminUser.getAdminUserId());
adminUserToken.setToken(token);
ValueOperations<String, AdminUserToken> setToken =
redisTemplate.opsForValue();
setToken.set(token, adminUserToken, 2 * 24 * 60 * 60, TimeUnit.SECONDS);
//过期时间为 48 小时
return token;
}
return "登录失败";
}
修改的类是 newbee-mall-cloud-user-web
模块下的 ltd.user.cloud.newbee.service.impl.AdminUserServiceImpl
类。
删除原有保存和修改 MySQL
数据库中 AdminUserToken
数据的逻辑代码,之后引入 RedisTemplate
对象,并且添加将 AdminUserToken
数据存储到 Redis
数据库中的代码。简单理解就是改变数据的存储方式,不过登录方法中生成 token
值并保存等待后续鉴权的底层思路没有太多变化。
登录方法的逻辑修改完成后,相应地修改鉴权逻辑。原来的逻辑是获取请求头中的 token
变量,并根据这个变量查询 MySQL
数据库是否存在和是否过期。现在引入了 Redis
数据库,就不需要查询 MySQL
数据库了,直接读取 Redis
数据库中是否有对应的 token
值即可。修改后的鉴权代码如下:
@Autowired
private RedisTemplate redisTemplate;
public Object resolveArgument(MethodParameter parameter,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) {
if (parameter.getParameterAnnotation(TokenToAdminUser.class) instanceof TokenToAdminUser) {
String token = webRequest.getHeader("token");
if (null != token && !"".equals(token) && token.length() == 32) {
ValueOperations<String, AdminUserToken> opsForAdminUserToken = redisTemplate.opsForValue();
AdminUserToken adminUserToken = opsForAdminUserToken.get(token);
if (adminUserToken == null) {
NewBeeMallException.fail("ADMIN_NOT_LOGIN_ERROR");
}
return adminUserToken;
} else {
NewBeeMallException.fail("ADMIN_NOT_LOGIN_ERROR");
}
}
return null;
}
修改的类是 newbee-mall-cloud-user-web
模块下的 ltd.user.cloud.newbee.service.config.handler.TokenToAdminUserMethodArgumentResolver
类。
删除原有查询 MySQL
数据库中 AdminUserToken
数据的逻辑代码,之后引入 RedisTemplate
对象,并且添加从 Redis
数据库中读取 AdminUserToken
数据的代码,后续逻辑并没有更改。
至此,对 tb_newbee_mall_admin_user_token
表的相关操作就可以全部删除了,直接从数据库中删除这张表及工程中对这张表操作的相关代码。这一步就比较简单了,删除 Mapper
文件及 dao
包中的接口即可。