SpringBoot实现token认证(基于缓存)

一、序言

本博客基于SpringBoot,使用redis缓存实现token认证,来验证用户身份的合法性。

二、什么是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表

SpringBoot实现token认证(基于缓存)_第1张图片

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");
    }

}

登录之后的结果:

SpringBoot实现token认证(基于缓存)_第2张图片

 使用其他接口进行测试:

SpringBoot实现token认证(基于缓存)_第3张图片

你可能感兴趣的:(Java,框架,缓存)