JWT刷新token+shrio+jwt 和小程序整合

后端

/**
 * JWT核心过滤器配置
 * 所有的请求都会先经过Filter,所以我们继承官方的BasicHttpAuthenticationFilter,并且重写鉴权的方法。
 * 执行流程 preHandle->isAccessAllowed->isLoginAttempt->executeLogin
 */
public class JwtFilter extends BasicHttpAuthenticationFilter {


    /**
     * logger
     */
    private static final Logger logger = LoggerFactory.getLogger(JwtFilter.class);

    /**
     * 判断用户是否想要进行 需要验证的操作
     * 检测header里面是否包含Authorization字段即可
     */
    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        String auth = getAuthzHeader(request);
        return auth != null && !auth.equals("");

    }

    /**
     * 此方法调用登陆,验证逻辑
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (isLoginAttempt(request, response)) {

            try {
                // 进行Shiro的登录jwtRealm
                JwtToken token = new JwtToken(getAuthzHeader(request));
                getSubject(request, response).login(token);
            } catch (AuthenticationException e) {
                try {
                    if (this.refreshToken(request, response)) {
                        return true;
                    }
                } catch (UnsupportedEncodingException ex) {
                    ex.printStackTrace();
                }
            }
        }else {
            // 没有携带Token
            HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
            // 获取当前请求类型
            String httpMethod = httpServletRequest.getMethod();
            // 获取当前请求URI
            String requestURI = httpServletRequest.getRequestURI();
            logger.info("当前请求 {} Authorization属性(Token)为空 请求类型 {}", requestURI, httpMethod);
//             mustLoginFlag = true 开启任何请求必须登录才可访问/
            Boolean mustLoginFlag = true;
            if (mustLoginFlag) {
                this.response401(response, "请先登录");
                return false;
            }
        }

        return true;
    }


    /**
     * 无需转发,直接返回Response信息
     */
    private void response401(ServletResponse response, String msg) {
        try {
            HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
            httpServletResponse.sendRedirect("/static/baodian/index.html");
        } catch (Exception e) {
            logger.error("直接返回Response信息出现IOException异常:" + e.getMessage());
            throw new CustomException("直接返回Response信息出现IOException异常:" + e.getMessage());
        }
//        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
//        httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
//        httpServletResponse.setCharacterEncoding("UTF-8");
//        httpServletResponse.setContentType("application/json; charset=utf-8");
//        try (PrintWriter out = httpServletResponse.getWriter()) {
//            String data = JsonConvertUtil.objectToJson(new RestResponse(HttpStatus.UNAUTHORIZED.value(), "无权访问(Unauthorized):" + msg, null));
//            out.append(data);
//        } catch (IOException e) {
//            logger.error("直接返回Response信息出现IOException异常:" + e.getMessage());
//            throw new CustomException("直接返回Response信息出现IOException异常:" + e.getMessage());
//        }
    }
    /**
     * 提供跨域支持
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        return super.preHandle(request, response);
    }

    /**
     * 此处为AccessToken刷新,进行判断RefreshToken是否过期,未过期就返回新的AccessToken且继续正常访问
     */
    private boolean refreshToken(ServletRequest request, ServletResponse response) throws UnsupportedEncodingException {
        // 拿到当前Header中Authorization的AccessToken(Shiro中getAuthzHeader方法已经实现)
        String token = getAuthzHeader(request);
        JwtConfig jwtConfig = SpringUtils.getBean("jwtConfig");
        // 当前Token的账号(手机号)
        String phone = jwtConfig.getPhoneByToken(token);
//        final String refreshToken = jwtConfig.createTokenByWxAccount(phone);
        // 判断Redis中RefreshToken是否存在
        if (JedisUtil.exists(RedisConstant.PREFIX_SHIRO_REFRESH_TOKEN_WX + phone)) {
            // Redis中RefreshToken还存在,获取RefreshToken的时间戳
            String JwtId = JedisUtil.getObject(RedisConstant.PREFIX_SHIRO_REFRESH_TOKEN_WX + phone).toString();
            // 获取当前AccessToken中的时间戳,与RefreshToken的时间戳对比,如果当前时间戳一致,进行AccessToken刷新
            if (jwtConfig.getJwtIdByToken(token).equals(JwtId)) {
                // 刷新AccessToken,设置时间戳为当前最新时间戳
                token = jwtConfig.createTokenByWxAccount(phone);
                // 获取当前最新jwtId
                String newJwtId = jwtConfig.getJwtIdByToken(token);
                // 读取配置文件,获取refreshTokenExpireTime属性
                PropertiesUtil.readProperties("config.properties");
                String accessTokenExpireTime = PropertiesUtil.getProperty("accessTokenExpireTime");

                // 设置AccessToken中的时间戳为当前最新时间戳,且刷新过期时间重新为30分钟过期(配置文件可配置refreshTokenExpireTime属性)
                JedisUtil.setObject(RedisConstant.PREFIX_SHIRO_ACCESS_TOKEN_WX + phone, newJwtId, Integer.parseInt(accessTokenExpireTime));


                // 设置RefreshToken中的时间戳为当前最新时间戳,且刷新过期时间重新为30分钟过期(配置文件可配置refreshTokenExpireTime属性)
                JedisUtil.setObject(RedisConstant.PREFIX_SHIRO_REFRESH_TOKEN_WX + phone, newJwtId);

                // 将新刷新的AccessToken再次进行Shiro的登录
                JwtToken jwtToken = new JwtToken(token);
                // 提交给UserRealm进行认证,如果错误他会抛出异常并被捕获,如果没有抛出异常则代表登入成功,返回true
                this.getSubject(request, response).login(jwtToken);
                //  最后将刷新的AccessToken存放在Response的Header中的Authorization字段返回
                HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
                //此处会重新请求你的url 并且返回给前端
                httpServletResponse.setHeader("Authorization", token);
                httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
                return true;
            }else {
                HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
                httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
                httpServletResponse.setCharacterEncoding("UTF-8");
                httpServletResponse.setContentType("application/json; charset=utf-8");
                try (PrintWriter out = httpServletResponse.getWriter()) {
                    String data = JsonConvertUtil.objectToJson(new RestResponse(HttpStatus.UNAUTHORIZED.value(), "无权访问", null));
                    out.append(data);
                    return true;
                } catch (IOException ez) {
                    logger.error("直接返回Response信息出现IOException异常:" + ez.getMessage());
                    throw new CustomException("直接返回Response信息出现IOException异常:" + ez.getMessage());
                }
            }
        }else {
            String msg = "Token已过期";
             //Token认证失败直接返回Response信息
            this.response401(response, msg);
        }
        return false;
    }
}

 

小程序端

var app = getApp();

const regeneratorRuntime = require("../regenerator/runtime");



function wxPromisify(fn) {

return function (obj = {}) {

return new Promise((resolve, reject) => {

obj.success = function (res) {

console.log(res)

if (res.header.Authorization!=null){

wx.setStorageSync("token", res.header.Authorization)

}



if (res.data.resultCode==401){

wx.removeStorageSync("userInfo")

wx.showModal({

title: '加载失败',

content: '请从新登录',

showCancel: false,

success: function (res) {

if (res.cancel) {

//点击取消,默认隐藏弹框

} else {

//点击确定

wx.navigateTo({

url: '../phone/phone',

})

}

}

})

}

//成功

resolve(res)

}

obj.fail = function (res) {

wx.hideLoading()

//失败

console.log(res)

reject(res)

}

fn(obj)

})

}

}

//无论promise对象最后状态如何都会执行

Promise.prototype.finally = function (callback) {

let P = this.constructor;

return this.then(

value => P.resolve(callback()).then(() => value),

reason => P.resolve(callback()).then(() => { throw reason })

);

};




/**

* 微信请求get方法

* url

* data 以对象的格式传入

*/

function getRequest(url, data) {

var getRequest = wxPromisify(wx.request)

return getRequest({

url: app.globalData.host + url,

method: 'GET',

data: data,

header: {

'Content-type': 'application/x-www-form-urlencoded',// 默认值,

'Authorization': wx.getStorageSync("token")

},

})

}



function uploadFile(url, filePath, formData,name){

var uploadFile = wxPromisify(wx.uploadFile)

return uploadFile({

// 模拟https

url: app.globalData.host + url, //需要用HTTPS,同时在微信公众平台后台添加服务器地址

filePath: filePath, //上传的文件本地地址

name: name,

formData: formData,

header: {

'Content-type': 'multipart/form-data',

'Authorization': wx.getStorageSync("token")

}

})

}






/**

* 微信请求post方法封装

* url

* data 以对象的格式传入

*/

function postRequest(url, data) {

var postRequest = wxPromisify(wx.request)

return postRequest({

url: app.globalData.host + url,

method: 'POST',

data: data,

header: {

'Content-type': 'application/x-www-form-urlencoded',// 默认值,

'Authorization': wx.getStorageSync("token")

},

})

}

/**

* 微信请求post方法封装

* url

* data 以对象的格式传入

*/

function postJsonRequest(url, data) {

var postRequest = wxPromisify(wx.request)

return postRequest({

url: app.globalData.host + url,

method: 'POST',

data: data,

header: {

'Content-type': 'application/json',// 默认值,

'Authorization': wx.getStorageSync("token")

},

})

}

/**

* 微信请求delete方法

* url

* data 以对象的格式传入

*/

function deleteRequest(url, data) {

var getRequest = wxPromisify(wx.request)

return getRequest({

url: app.globalData.host + url,

method: 'delete',

header: {

'Content-type': 'application/x-www-form-urlencoded',// 默认值,

'Authorization': wx.getStorageSync("token")

},

})

}



/**

* 微信请求put方法

* url

* data 以对象的格式传入

*/

function putRequest(url, data) {

var getRequest = wxPromisify(wx.request)

return getRequest({

url: app.globalData.host + url,

method: 'PUT',

data: data,

header: {

'Content-type': 'application/x-www-form-urlencoded',// 默认值,

'Authorization': wx.getStorageSync("token")

},

})

}

/**

* 微信请求putJson方法

* url

* data 以对象的格式传入

*/

function putJsonRequest(url, data) {

var getRequest = wxPromisify(wx.request)

return getRequest({

url: app.globalData.host + url,

method: 'PUT',

data: data,

header: {

'Content-type': 'application/json',// 默认值,

'Authorization': wx.getStorageSync("token")

},

})

}

module.exports = {

postRequest,

getRequest,

deleteRequest,

putRequest,

postJsonRequest,

putJsonRequest,

uploadFile

}

写的是两部分核心的东西其他的 网上都一样

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值