通过SpringBoot整合Redis(二 ) springboot整合缓存redis 对redis有了初步的理解。接下来探索一下如何运用Redis存取、识别当前登录用户信息。
本次实现:
1.用户登录,校验用户名密码后,登录成功产生token值,保存入Redis中,设置时效50分钟。
2.使用拦截器/ SpringAop技术,当用户调用其他接口时,必须传入token值,并且比较token值是否有效(存在),是否正确。
约定 :
一个用户只能拥有一个token,用户重复登录时,也就是说用户登录的token还有效时,用户再次登录,此时token有效期重置。
存入Redis格式为 : adminId : token,当用户调用除登录接口外的其他接口(我这测试只考虑了两个接口),进行校验,先校验adminId是否存在,再校验token是否正确。
1.登录成功返回信息
2. 调用其他接口返回信息
依赖信息和Redis、数据库连接的配置信息,上一篇博客中有 SpringBoot整合Redis(二 ) springboot整合缓存redis
DROP TABLE IF EXISTS `jc_user`;
CREATE TABLE `jc_user` (
`id` tinyint(11) NOT NULL,
`user_name` varchar(150) NOT NULL COMMENT '用户名',
`password` varchar(150) DEFAULT NULL,
`user_age` tinyint(11) DEFAULT NULL COMMENT '用户年龄',
`user_sex` tinyint(11) NOT NULL COMMENT '用户性别(1表示男、2表示女、3表示未知)',
`city` varchar(150) NOT NULL,
`deleted_flag` tinyint(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
工具类
1. JSON / 对象转换类
package user.login.utils;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* JSON工具类
* 将json和对象互相转换
* @author wulongwei
*
*/
public class JsonUtils {
// 定义jackson对象
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 将对象转换成json字符串。
*
* @param data
* @return
*/
public static String objectToJson(Object data) {
try {
String string = MAPPER.writeValueAsString(data);
return string;
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* 将json结果集转化为对象
*
* @param jsonData
* @param beanType
* @return
*/
public static T jsonToPojo(String jsonData, Class beanType) {
try {
T t = MAPPER.readValue(jsonData, beanType);
return t;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 将json数据转换成pojo对象list
*
* @param jsonData
* @param beanType
* @return
*/
public static List jsonToList(String jsonData, Class beanType) {
JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
try {
List list = MAPPER.readValue(jsonData, javaType);
return list;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2.生成token类,登录成功后调用,如果同一用户重复登录,token的时效重置
package user.login.utils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
/**
* 生产token,如果同一用户重复登录,token的时效重置
* @author ASUS
*
*/
@Service
public class ProductToken {
/**
* 生成有时效的token
* 之后调其他接口需要校验adminId和token值
* @param key
* @param value
*/
public Map productToken(String key, String value) {
Map infoMap = new HashMap();
if(redisTemplate.opsForValue().get(key) == null) {
//将登陆的信息保存如redis
redisTemplate.opsForValue().set(key, value);
infoMap.put(key, value);
}
//设置token有效的时间
redisTemplate.expire(key, 3000, TimeUnit.SECONDS);
return infoMap;
}
@Autowired
private StringRedisTemplate redisTemplate;
}
接口响应类 (调用接口后用统一的格式进行返回)
package user.login.base;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
// 服务接口有响应 统一规范响应服务接口信息
@Data
@Slf4j
public class ResponseBase {
private Integer rtnCode;
private String msg;
private Object data;
public ResponseBase() {
}
public ResponseBase(Integer rtnCode, String msg, Object data) {
super();
this.rtnCode = rtnCode;
this.msg = msg;
this.data = data;
}
public static void main(String[] args) {
ResponseBase responseBase = new ResponseBase();
responseBase.setData("123456");
responseBase.setMsg("success");
responseBase.setRtnCode(200);
System.out.println(responseBase.toString());
log.info("wulongwei...");
}
@Override
public String toString() {
return "ResponseBase [rtnCode=" + rtnCode + ", msg=" + msg + ", data=" + data + "]";
}
}
package user.login.base;
public class BaseApiService {
public ResponseBase setResultError(Integer code, String msg) {
return setResult(code, msg, null);
}
// 返回错误,可以传msg
public ResponseBase setResultError(String msg) {
return setResult(500, msg, null);
}
// 返回成功,可以传data值
public ResponseBase setResultSuccess(Object data) {
return setResult(200, "处理成功", data);
}
// 返回成功,沒有data值
public ResponseBase setResultSuccess() {
return setResult(200, "处理成功", null);
}
// 返回成功,沒有data值
public ResponseBase setResultSuccess(String msg) {
return setResult(200, msg, null);
}
// 通用封装
public ResponseBase setResult(Integer code, String msg, Object data) {
return new ResponseBase(code, msg, data);
}
}
实体类
package user.login.entity;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class User {
private Integer userId;
private String userName;
private String password;
private Integer userAge;
private Short userSex;
private String city;
}
拦截器:在每次调用接口时进行拦截,校验用户传递的adminId和token是否有效并且正确
package user.login.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import user.login.interceptor.UserInterceptor;
@Configuration //适配器
public class WebMvcConfigurer extends WebMvcConfigurationSupport {
@Bean
public UserInterceptor getUserInterceptor() {
return new UserInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
/**
* 拦截器按照顺序执行
*/
registry.addInterceptor(getUserInterceptor()).excludePathPatterns("/user/login");
super.addInterceptors(registry);
}
}
package user.login.interceptor;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import user.login.base.ResponseBase;
import user.login.entity.dto.TokenInfo;
import user.login.utils.JsonUtils;
public class UserInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 在请求处理之前进行调用(Controller方法调用之前)
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) {
// 检查用户传递的 token是否合法
TokenInfo tokenInfo = this.getUserToKen(request);
if (StringUtils.isBlank(tokenInfo.getAdminId()) && StringUtils.isBlank(tokenInfo.getToken())) {
// 返回登录
System.out.println("没有传入对应的身份信息,返回登录");
return false;
}
try {
String token = redisTemplate.opsForValue().get(tokenInfo.getAdminId());
if (token != null && token.equals(tokenInfo.getToken())) {
System.out.println("校验成功");
return true;
} else {
System.out.println("校验失败,返回登录");
return false;
}
} catch (Exception e) {
System.out.println("校验失败,对呀的信息匹配错误,返回登录");
return false;
}
}
/**
* 在cookie中获取用户传递的token
*/
private TokenInfo getUserToKen(HttpServletRequest request) {
TokenInfo info = new TokenInfo();
String adminId = request.getHeader("adminId");
String token = request.getHeader("token");
if (StringUtils.isNotBlank(adminId) && StringUtils.isNotBlank(token)) {
info.setAdminId(adminId);
info.setToken(token);
}
return info;
}
/**
* 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView mv)
throws Exception {
}
/**
* 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行 (主要是用于进行资源清理工作)
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception ex)
throws Exception {
}
public void returnErrorResponse(HttpServletResponse response, ResponseBase result)
throws IOException, UnsupportedEncodingException {
OutputStream out = null;
try {
response.setCharacterEncoding("utf-8");
response.setContentType("text/json");
out = response.getOutputStream();
out.write(JsonUtils.objectToJson(result).getBytes("utf-8"));
out.flush();
} finally {
if (out != null) {
out.close();
}
}
}
}
控制器层
package user.login.controller;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.demo.entity.UserDto;
import user.login.base.ResponseBase;
import user.login.service.UserService;
@RestController
@RequestMapping(value = "/user")
public class UserController {
/**
* 登陆成功返回token给前台,之后每调一个接口都需要校验token之后有效
* @param username
* @param passwrod
* @return
*/
@PostMapping("/login")
public ResponseBase UserLogin(@RequestBody @Valid UserDto userDto, HttpServletRequest request) {
return userService.UserLogin(userDto.getUserName(), userDto.getPassword());
}
/**
* 查看用户的基本信息
* @param id
* @return
*/
@GetMapping("/getUserInfo")
public ResponseBase getUserInfo(Integer id) {
return userService.getUserInfo(id);
}
@Autowired
private UserService userService;
}
service实现
package user.login.service.impl;
import java.util.Map;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import redis.demo.entity.User;
import user.login.base.BaseApiService;
import user.login.base.ResponseBase;
import user.login.mapper.UserMapper;
import user.login.service.UserService;
import user.login.utils.ProductToken;
@Service
@Transactional
public class UserServiceImpl extends BaseApiService implements UserService{
@Override
public ResponseBase UserLogin(String username, String passwrod) {
//1.校验登陆是否成功
User user = userMapper.getUserInfo(username, passwrod);
//2.如果不成功返回提示
if (user == null) {
return setResultError("账户名密码错误!!!");
} else {
//3.如果成功,生产一个token
String token = UUID.randomUUID().toString().replaceAll("-", "");
Map mapInfo = productToken.productToken(user.getId().toString(), token);
//3.返回token信息(有效期50分钟)
return setResultSuccess((Object)mapInfo);
}
}
@Override
public ResponseBase getUserInfo(Integer userId) {
User user = userMapper.getUser(userId);
if(user != null) {
return setResultSuccess((Object)user);
} else {
return setResultSuccess("无此用户");
}
}
@Autowired
private ProductToken productToken;
@Autowired
private UserMapper userMapper;
}
mapper层
package user.login.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.ResultMap;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.SelectProvider;
import org.springframework.stereotype.Repository;
import redis.demo.entity.User;
import user.login.mapper.sql.UserSqlProvider;
@Repository
@Mapper
public interface UserMapper {
/**
* 通过用户名、密码获取用户信息
* @param userName
* @param password
* @return
*/
@SelectProvider(method = "select", type = UserSqlProvider.class)
@Results(id="user", value={
@Result(column="id", property="userId", id=true),
@Result(column="user_name", property="userName"),
@Result(column="user_age", property="userAge"),
@Result(column="user_sex", property="userSex"),
@Result(column="city", property="city")
})
User getUserInfo(String userName, String password);
/**
* 通过用户ID查询用户基本信息
* @param userId
* @return
*/
@SelectProvider(method = "selectById", type = UserSqlProvider.class)
@ResultMap(value="user")
User getUser(Integer userId);
}
package user.login.mapper.sql;
import org.apache.ibatis.jdbc.SQL;
public class UserSqlProvider {
public String select(final String userName, final String password) {
String sql = new SQL() {
{
SELECT("*");
FROM("jc_user");
WHERE("deleted_flag = false AND user_name='" + userName + "' AND password= '" + password + "'");
}
}.toString();
return sql;
}
public String selectById(final Integer userId) {
String sql = new SQL() {
{
SELECT("*");
FROM("jc_user");
WHERE("deleted_flag = false AND id= '" + userId + "'");
}
}.toString();
return sql;
}
}
启动类
@SpringBootApplication
@MapperScan(basePackages= {"user.login.mapper"})
public class app {
public static void main(String[] args) {
SpringApplication.run(app.class, args);
}
}
代码链接
链接:https://pan.baidu.com/s/1YQC5MwfR4q3ckVgm1epalg
提取码:eagh