spring boot和redis实现自定义前后分离token认证

说明:文章部分代码引用自github

本项目地址:https://gitee.com/indexman/redis-token-demo

1.token认证流程

此处以前端页面请求后端用户列表接口为例:
在这里插入图片描述

2.用到的技术

  • redis:存储用户及token信息
  • localstorage:前端存储获取到的token
  • 自定义拦截器:用于拦截和校验HTTP请求中token的有效性

3.实现效果展示

  • 登录获取token

在这里插入图片描述

  • 查看redis中token信息

在这里插入图片描述

  • token失效后跳转到登录页

在这里插入图片描述

4.核心源码展示

4.1 token鉴权接口

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthToken {

}

4.1 自定义拦截器

@Slf4j
public class AuthorizationInterceptor implements HandlerInterceptor {


    //存放鉴权信息的Header名称,默认是Authorization
    private String httpHeaderName = "Authorization";

    //鉴权失败后返回的错误信息,默认为401 unauthorized
    private String unauthorizedErrorMessage = "401 unauthorized";

    //鉴权失败后返回的HTTP错误码,默认为401
    private int unauthorizedErrorCode = HttpServletResponse.SC_UNAUTHORIZED;

    /**
     * 存放登录用户模型Key的Request Key
     */
    public static final String REQUEST_CURRENT_KEY = "REQUEST_CURRENT_KEY";


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method        method        = handlerMethod.getMethod();
        // 如果打上了AuthToken注解则需要验证token
        if (method.getAnnotation(AuthToken.class) != null || handlerMethod.getBeanType().getAnnotation(AuthToken.class) != null) {


            String token = request.getHeader(httpHeaderName);
            log.info("token is {}", token);
            String username = "";
            Jedis  jedis    = new Jedis("localhost", 6379);
            if (token != null && token.length() != 0) {
                username = jedis.get(token);
                log.info("username is {}", username);
            }
            if (username != null && !username.trim().equals("")) {
                //log.info("token birth time is: {}",jedis.get(username+token));
                Long tokeBirthTime = Long.valueOf(jedis.get(token + username));
                log.info("token Birth time is: {}", tokeBirthTime);
                Long diff = System.currentTimeMillis() - tokeBirthTime;
                log.info("token is exist : {} ms", diff);
                if (diff > ConstantKit.TOKEN_RESET_TIME) {
                    jedis.expire(username, ConstantKit.TOKEN_EXPIRE_TIME);
                    jedis.expire(token, ConstantKit.TOKEN_EXPIRE_TIME);
                    log.info("Reset expire time success!");
                    Long newBirthTime = System.currentTimeMillis();
                    jedis.set(token + username, newBirthTime.toString());
                }

                //用完关闭
                jedis.close();
                request.setAttribute(REQUEST_CURRENT_KEY, username);
                return true;


            } else {
                JSONObject jsonObject = new JSONObject();

                PrintWriter out = null;
                try {
                    response.setStatus(unauthorizedErrorCode);
                    response.setContentType(MediaType.APPLICATION_JSON_VALUE);

                    jsonObject.put("code", ((HttpServletResponse) response).getStatus());
                    jsonObject.put("message", HttpStatus.UNAUTHORIZED);
                    out = response.getWriter();
                    out.println(jsonObject);

                    return false;
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (null != out) {
                        out.flush();
                        out.close();
                    }
                }

            }

        }

        request.setAttribute(REQUEST_CURRENT_KEY, null);

        return true;
    }

4.3 用户controller

@RestController
@Slf4j
@RequestMapping("/api/user")
public class UserController {

    @Autowired
    Md5TokenGenerator tokenGenerator;

    @Autowired
    UserMapper userMapper;

    @RequestMapping(value = "login", method = RequestMethod.POST)
    @ApiOperation("用户登录接口")
    public ResponseTemplate login(@RequestBody(required = false) JSONObject userInfo) {

        String username = userInfo.getString("username");
        String password = userInfo.getString("password");

        List<User> users = userMapper.selectList(new EntityWrapper<User>()
                .eq("username", username)
                .eq("password", password));
        JSONObject result = new JSONObject();

        if(users.size()>0){
            User currentUser = users.get(0);
            if(currentUser!=null){
                Jedis  jedis = new Jedis("localhost", 6379);
                String token = tokenGenerator.generate(username, password);
                jedis.set(username, token);
                jedis.expire(username, ConstantKit.TOKEN_EXPIRE_TIME);
                jedis.set(token, username);
                jedis.expire(token, ConstantKit.TOKEN_EXPIRE_TIME);
                Long currentTime = System.currentTimeMillis();
                jedis.set(token + username, currentTime.toString());

                //用完关闭
                jedis.close();

                result.put("code",200);
                result.put("status", "登录成功");
                result.put("token", token);
            }
        }else{
            result.put("code",400);
            result.put("status", "登录失败");
            result.put("token", "");
        }


        return ResponseTemplate.builder()
                .code(result.getInteger("code"))
                .message(result.getString("status"))
                .data(result.getString("token"))
                .build();

    }


    @ApiOperation("查询用户列表")
    @RequestMapping(value = "listAll", method = RequestMethod.GET)
    @AuthToken
    public ResponseTemplate listAll() {
        List<User> user = new User().selectAll();
        return ResponseTemplate.builder()
                .code(200)
                .message("Success")
                .data(user)
                .build();
    }
}

4.4 登录页

$(function() {
        $("#login-btn").click(function (event) {
            // 阻止表单默认提交
            event.preventDefault();

            var username = $("#username").val(), password = $("#password").val();
            if(username==""){
                alert("用户名不能为空!");
                return;
            }
            if(password==""){
                alert("密码不能为空!");
                return;
            }

            var param={"username": username, "password": password}

            // 提交验证
            $.ajax({
                type: "POST",
                url: "/api/user/login",
                contentType: "application/json",
                data: JSON.stringify(param),
                success: function (result) {
                    console.log(result)
                    if(result.code==200){
                        window.localStorage.setItem("token",result.data)
                        window.location.href="users.html";
                    }else{
                        alert(result);
                    }
                }
            });


        });
    });

4.5 用户列表页

$(function() {
            var token = window.localStorage.getItem("token")
            console.log(token)
            // 提交验证
            $.ajax({
                type: "GET",
                url: "/api/user/listAll",
                headers:{'Authorization':token},
                contentType: "application/json",
                success: function (result) {
                    console.log(result)
                    if(result.code==200){
                        if (result.data != null && result.data!='') {
                            // 拼接列表
                            var dataRow = '<tr><td>ID</td><td>用户名</td><td>密码</td></tr>';
                            $.each(result.data, function (i, r) {
                                dataRow += '<tr>'
                                    + '<td>'
                                    + r.id
                                    + '</td>'
                                    + '<td>'
                                    + r.username
                                    + '</td><td>'
                                    + r.password + '</td>'
                                ;

                                dataRow += '</tr>';
                            });

                            // console.log(dataRow);
                            $("#tb-users").empty();
                            $("#tb-users").append(dataRow);
                        }
                    }else{
                        alert(result);
                    }
                },
                error:function(result){
                    if(result.status==401){
                        window.location.href="login.html";
                    }
                }
            });
        });
posted @ 2020-04-01 22:44  一锤子技术员  阅读(3)  评论(0编辑  收藏  举报  来源