本博客基于SpringBoot,使用redis缓存实现token认证,来验证用户身份的合法性。
token意为令牌,为一个随机的字符串UUID生成,用来标记来访用户的身份,通过该token,可以得出是哪一个用户向我服务器访问资源。
当用户登录成功之后,则向客户端发送token,用来标记该用户,以后每次用户向服务器访问时,则在请求头带上该token。服务器从请求头中获取token,拿到该token之后,向redis缓存查找是否存在此token,如存在,则返回给用户相应的资源,若不存在,则返回,不给与资源。
1.引入Maven依赖
4.0.0
org.springframework.boot
spring-boot-starter-parent
1.5.21.RELEASE
com.chen
cachetest
0.0.1-SNAPSHOT
cachetest
Demo project for Spring Boot
UTF-8
UTF-8
1.8
org.springframework.boot
spring-boot-starter-jdbc
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.0.1
org.springframework.boot
spring-boot-starter-cache
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-test
test
junit
junit
4.12
test
org.springframework.boot
spring-boot-test
2.1.6.RELEASE
test
org.springframework.boot
spring-boot-starter-data-redis
org.projectlombok
lombok
org.json
json
commons-io
commons-io
org.springframework.boot
spring-boot-maven-plugin
2.创建user表
3.编写application.properties
#配置数据源
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
#开启驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true
#配置redis
spring.redis.host=localhost
spring.redis.port=6379
#打印SQL语句日志
logging.level.com.chen.mapper=debug
4.编写用户类User
package com.chen.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
int id;
String username;
String password;
}
5.编写Mapper
package com.chen.mapper;
import com.chen.bean.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
@Mapper
public interface UserMapper {
@Select("SELECT * FROM `user` WHERE username=#{username} AND password=#{password}")
public User getUser(User user);
}
6.编写Service
package com.chen.service;
import com.chen.bean.User;
import com.chen.mapper.UserMapper;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public User getUser(User user) {
User u = userMapper.getUser(user);
return u;
}
}
7.编写Msg类,用来封装返回的信息
package com.chen.util;
import java.util.HashMap;
import java.util.Map;
public class Msg {
// 状态码: 200-成功 400-失败 500-异常
private int status;
// 提示信息
private String message;
// 用户返回给浏览器的数据
private Map data = new HashMap();
public static Msg success() {
Msg result = new Msg();
result.setStatus(200);
result.setMessage("处理成功!");
return result;
}
public static Msg fail() {
Msg result = new Msg();
result.setStatus(400);
result.setMessage("处理失败!");
return result;
}
public static Msg error() {
Msg result = new Msg();
result.setStatus(500);
result.setMessage("未知异常!");
return result;
}
public Msg add(String key, Object value) {
this.data.put(key, value);
return this;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Map getData() {
return data;
}
public void setData(Map data) {
this.data = data;
}
}
8.编写Controller
package com.chen.controller;
import com.chen.bean.User;
import com.chen.service.UserService;
import com.chen.util.Msg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@RestController
public class UserController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Autowired
private UserService userService;
@PostMapping("/login")
public Msg login(User user) {
User u = userService.getUser(user);
if (u != null) {
String token = UUID.randomUUID().toString().replaceAll("-", "");
stringRedisTemplate.opsForValue().set(token, String.valueOf(u.getId()), 3600, TimeUnit.SECONDS);//将用户的ID信息存入redis缓存,并设置一小时的过期时间
return Msg.success().add("token",token).add("info","登录成功");
}else {
return Msg.fail().add("info", "登录失败");
}
}
@PostMapping("/other")
public Msg other() {
return Msg.success().add("info", "该接口是来测试的");
}
}
9.配置启动类,使mapper生效
package com.chen;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@MapperScan("com.chen.mapper")
@SpringBootApplication
@EnableCaching
public class CachetestApplication {
public static void main(String[] args) {
SpringApplication.run(CachetestApplication.class, args);
}
}
10.设置拦截器,在进行接口访问前,先验证token的正确性
package com.chen.interceptor;
import com.chen.util.Msg;
import org.json.JSONObject;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Component
public class ParamInterceptor implements HandlerInterceptor {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
String token = httpServletRequest.getHeader("token");
//token验证
if(token!=null){
String id = stringRedisTemplate.opsForValue().get(token);
if(id!=null){
System.out.println("token验证成功");
return true;
}else{
System.out.println("token验证失败");
returnJson(httpServletResponse);
return false;
}
}else{
System.out.println("token验证失败");
returnJson(httpServletResponse);
return false;
}
}
private void returnJson(HttpServletResponse response){
PrintWriter writer = null;
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
try {
writer = response.getWriter();
Msg msg = Msg.fail().add("info", "没有token或token无效");
JSONObject jsonObject = new JSONObject(msg);
writer.print(jsonObject);
} catch (IOException e){
e.printStackTrace();
} finally {
if(writer != null){
writer.close();
}
}
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
11.配置拦截器,使前一步设置的拦截器生效
package com.chen.config;
import com.chen.interceptor.ParamInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private ParamInterceptor paramInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
//此处配置拦截路径
registry.addInterceptor(paramInterceptor).addPathPatterns("/**").excludePathPatterns("/login");
}
}
登录之后的结果:
使用其他接口进行测试: