引言

在前面实现了「注册」和「登录」的功能,本文主要讲解“登出功能”,虽然功能看上去比较简单,但是遇到了不少的坑。

1.功能演示及实现思路

实现思路:

  1. 会员登录成功后,点击主页面右上角的退出,请求后台
  2. 后台删除Cookie信息里面的token
  3. 后台根据token删除Redis里保存的token
  4. 后台根据token更新数据库里面对应的token记录

功能演示:

  1. 首先登录,登录成功后,界面如下:
    在这里插入图片描述

2.登录成功后,可以看到Redis和数据库均有内容:

redis
在这里插入图片描述
数据库
在这里插入图片描述
3.点击右上角的退出,点击完后,可以看到自动跳转到主页面:

点击前
在这里插入图片描述点击后
在这里插入图片描述
4.同时Redis和数据库里的内容均有改变:

redis
在这里插入图片描述
数据库
在这里插入图片描述

2. 登出功能

2.1 前端代码

前端使用的是jq的ajax请求,使用的是RESTful标准请求,代码如下:

html代码:

 <li th:if="${desensMobile!=null}"><a href="javascript:void(0);" onclick="logout();">退出</a></li>

js代码:

    <!--引入JQuery-->
    <script type="text/javascript" src="plugins/jquery/jquery-1.12.4.min.js"></script>
    <script type="text/javascript">
        function logout() {
            $.ajax({
                type: "delete",
                url: "exit",
                contentType: "application/json",
                dataType: "json",
                success: function (result) {
                    window.location.href = "/";
                },
                error: function (result) {
                }
            });
        }
    </script>

2.2 后端代码

MemberLogoutServiceFeign

package com.guoranxinxian.member.feign;

import com.guoranxinxian.service.MemberLogOutService;
import org.springframework.cloud.openfeign.FeignClient;

@FeignClient("guoranxinxian-shop-service-member")
public interface MemberLogoutServiceFeign extends MemberLogOutService {

}

控制层(使用的是Feign远程调用):

package com.guoranxinxian.member.controller;

import com.alibaba.fastjson.JSONObject;
import com.guoranxinxian.api.BaseResponse;
import com.guoranxinxian.common.base.BaseWebController;
import com.guoranxinxian.common.constants.WebConstants;
import com.guoranxinxian.common.util.CookieUtils;
import com.guoranxinxian.member.feign.MemberLogoutServiceFeign;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Controller
public class LogoutController extends BaseWebController {

    @Autowired
    private MemberLogoutServiceFeign memberLogoutServiceFeign;


    @DeleteMapping("/exit")
    @ResponseBody
    public BaseResponse<JSONObject> exit(HttpServletRequest request, HttpServletResponse response, Model model) {
        // 1.从cookie 中 获取 会员token
        String token = CookieUtils.getCookieValue(request, WebConstants.LOGIN_TOKEN_COOKIENAME, true);
        CookieUtils.deleteCookie(request, response, WebConstants.LOGIN_TOKEN_COOKIENAME);
        if (!StringUtils.isEmpty(token)) {
            // 2.调用登出服务接口
            return memberLogoutServiceFeign.logout(token);
        }
        return null;
    }
}


MemberLogOutService

package com.guoranxinxian.service;

import com.alibaba.fastjson.JSONObject;
import com.guoranxinxian.api.BaseResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

/**
 * description: 用户登出接口
 */
@Api(tags = "用户登出服务接口")
public interface MemberLogOutService {
    /**
     * 用户登出接口
     *
     * @param token
     * @return
     */
    @DeleteMapping("/logout")
    @ApiOperation(value = "/logout")
    BaseResponse<JSONObject> logout(@RequestParam("token") String token);

}

MemberLogOutServiceImpl

package com.guoranxinxian.impl;

import com.alibaba.fastjson.JSONObject;
import com.guoranxinxian.api.BaseResponse;
import com.guoranxinxian.entity.BaseApiService;
import com.guoranxinxian.mapper.UserTokenMapper;
import com.guoranxinxian.service.MemberLogOutService;
import com.guoranxinxian.util.GenerateToken;
import com.guoranxinxian.util.RedisDataSoureceTransaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.TransactionStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MemberLogOutServiceImpl extends BaseApiService<JSONObject> implements MemberLogOutService {

    @Autowired
    private GenerateToken generateToken;

    @Autowired
    private UserTokenMapper userTokenMapper;
    /**
     * 手动事务工具类
     */
    @Autowired
    private RedisDataSoureceTransaction manualTransaction;

    @Override
    public BaseResponse<JSONObject> logout(String token) {
        TransactionStatus transactionStatus = null;
        try {
            transactionStatus = manualTransaction.begin();
            //2.删除Redis
            generateToken.removeToken(token);
            //1.首先更新数据库
            int updateTokenAvailability = userTokenMapper.updateTokenAvailability(token);
            if (updateTokenAvailability < 0) {
                manualTransaction.rollback(transactionStatus);
                return setResultError("系统错误");
            }
            manualTransaction.commit(transactionStatus);
            JSONObject data = new JSONObject();
            data.put("result", true);
            return setResultSuccess(data);
        } catch (Exception e) {
            try {
                // 回滚事务
                manualTransaction.rollback(transactionStatus);
            } catch (Exception e1) {
            }
            return setResultError("系统错误!");
        }
    }
}

3. 遇到的坑

3.1 JQ ajax请求后台不能“请求转发”或者“重定向”

调用后台登出接口的时候,如果直接返回redirect:/,前端页面是不会直接跳转到的,这是为什么呢?

因为JQ使用ajax是禁止后台执行“请求转发”或者“重定向”的,只能后台返回数据,由前端使用window.location.href执行页面的跳转。如下代码:

$.ajax({
         type: "delete",
         url: "exit",
         contentType: "application/json",
         dataType: "json",
         success: function (result) {
             window.location.href = "/";
         },
         error: function (result) {
         }
     });
 }
);

3.2 JQ ajax 返回格式的指定格式

在上面代码,可以看到,ajax指定返回的格式为json,因此别忘记了在接口处添加@ResponseBody注解:
在这里插入图片描述

4.总结

本文主要讲解了 「登出」 的功能,虽然流程不复杂,但是里面涉及到了很多的基础知识,如RESTfulJQ ajax请求注意细节等。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐