04 动力云客之登录后获取用户信息+JWT存进Redis+Filter验证Token + token续期

这篇具有很好参考价值的文章主要介绍了04 动力云客之登录后获取用户信息+JWT存进Redis+Filter验证Token + token续期。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

04 动力云客之登录后获取用户信息+JWT存进Redis+Filter验证Token + token续期,动力云客项目,spring boot

1. 登录后获取用户信息

非常好实现. 只要新建一个controller, 并调用SS提供的Authentication对象即可

package com.sunsplanter.controller;

@RestController
public class UserController {

    @GetMapping(value = "api/login/info")
    public R loginInfo(Authentication authentication) {
        TUser tUser = (TUser)authentication.getPrincipal();

        return R.OK(tUser);
    }

}

未登录状态下可以直接访问 api/login/info吗?

不可以. 因为在安全配置类已经写明了, 仅登陆界面允许任何人访问, 其他所有界面都需要认证

<由于未写JWT, 默认使用Session 保存会话,> ???好像不对
因此只要我们先通过登录接口登录, 然后再直接访问获取用户信息接口即可
04 动力云客之登录后获取用户信息+JWT存进Redis+Filter验证Token + token续期,动力云客项目,spring boot

2. 使用JWT打通登录后各个页面的认证

前后端分离的项目一般会使用token(jwt)实现登录状态的保持;(java web : session)

token其实就是一个随机字符串(字符串要求是唯一的,不同人的token都不能相同),当用户在登录页面输入账号和密码后,前端将账号密码发送给后端,后端检验完账号和密码后,会生成一个随机不重复的字符串即(token),并将其响应给前端,前端拿到token后,需要在客户端进行持久化存储(一般会写在localStorage或者sessionStorage中),那么下次在向后端数据接口发送请求的时候,一般需要将token一并发送给后端数据接口,后端数据接口会对token进行校验,如果合法则正常响应请求,如果不合法,则提示未登录。

2.1 sessionStorage与localStorage

它们是javascript对象,浏览器支持这两个对象,可以直接使用. 属于前端范畴
localStorage和sessionStorage都是用来在浏览器客户端存储临时信息的对象;

sessionStorage、localStorage区别?

sessionStorage只在一个浏览器页面有效,比如你打开一个新的tab浏览器页会失效,你关闭浏览器后,再打开浏览器也会失效;
localStorage在整个浏览器中都有效,重启浏览器也有效;除非你手动删除了localStorage,才会失效;

2.2 修改登录成功拦截器

根据流程图, 在查询到对象并返回给SS后, SS会调用成功拦截器,
就在这个拦截器中, 根据查询到的对象生成JWT并同时存进Redis和返回前端.

//登录成功会自动执行这个类中的onAuthenticationSuccess方法, 该方法返回自定义的Json给前端
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Resource
    private RedisService redisService;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //登录成功,执行该方法,在该方法中返回json给前端,就行了
        TUser tUser = (TUser) authentication.getPrincipal();

        //1.生成JWT
        //由于createJWT方法定义的参数是序列化后的对象, 因此先调用JSONUtils序列化对象
        String userJSON = JSONUtils.toJSON(tUser);
        String jwt = JWTUtils.createJWT(userJSON);

        //2.写入Redis
        redisService.setValue(Constants.REDIS_JWT_KEY + tUser.getId(), jwt);

        //3. 设置JWT的过期时间(如果选择记住我, 过期时间是7天, 否则30分钟)
        String rememberMe = request.getParameter("rememberMe");
        //勾选了记住我就设置为7天, 其中EXPIRE_TIME就是7天,DEFAULT_EXPIRE_TIME是半小时
        if (Boolean.parseBoolean(rememberMe)){
            redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId() , Constants.EXPIRE_TIME, TimeUnit.SECONDS);
        } else {
            redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId() , Constants.DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
        }

        //登录成功的统一结果
        R result = R.OK(jwt);

        //把R对象转成json
        String resultJSON = JSONUtils.toJSON(result);

        //把R以json返回给前端
        ResponseUtils.write(response, resultJSON);
    }
}

常量类为

package com.sunsplanter.constant;

/**
 * 常量类
 */
public class Constants {

    public static final String LOGIN_URI = "/api/login";

    //redis的key的命名规范: 项目名:模块名:功能名:唯一业务参数(比如用户id)
    public static final String REDIS_JWT_KEY = "dlyk:user:login:";

    //redis中负责人的key
    public static final String REDIS_OWNER_KEY = "dlyk:user:owner";

    //jwt过期时间7天
    public static final Long EXPIRE_TIME = 7 * 24 * 60 * 60L;

    //jwt过期时间30分钟
    public static final Long DEFAULT_EXPIRE_TIME = 30 * 60L;
}

redis类 为

package com.sunsplanter.service

public interface RedisService {

    void setValue(String key, Object value);

    Object getValue(String key);

    Boolean removeValue(String key);

    //给jwt设置过期时间
    Boolean expire(String key, Long timeOut, TimeUnit timeUnit);
}
package com.sunsplanter.service.impl;

@Service
public class RedisServiceImpl implements RedisService {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void setValue(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    @Override
    public Object getValue(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    public Boolean removeValue(String key) {
        return redisTemplate.delete(key);
    }

    //给JWT设置过期时间
    @Override
    public Boolean expire(String key, Long timeOut, TimeUnit timeUnit) {
        return redisTemplate.expire(key, timeOut, timeUnit);
    }
}

在网页登录后可以看到已经存到了localstorage
04 动力云客之登录后获取用户信息+JWT存进Redis+Filter验证Token + token续期,动力云客项目,spring boot

3 Filter验证Token

如流程图所示, 第一次登录成功过后, 往后每次登录会携带token登录,

因此, 在config.filter文件夹下新建一个token过滤器类, 该类实现OncePerRequestFilter
并在SS的安全配置类中中添加进去

package com.sunsplanter.config.filter;

@Component
public class TokenVerifyFilter extends OncePerRequestFilter{

    @Resource
    private RedisService redisService;

    //spring boot框架的ioc容器中已经创建好了该线程池,可以注入直接使用
    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (request.getRequestURI().equals(Constants.LOGIN_URI)) { //如果是登录请求,此时还没有生成jwt,那不需要对登录请求进行jwt验证
            //验证jwt通过了 ,让Filter链继续执行,也就是继续执行下一个Filter
            filterChain.doFilter(request, response);

        } else {
            String token = null;
            if (request.getRequestURI().equals(Constants.EXPORT_EXCEL_URI)) {
                //从请求路径的参数中获取token
                token = request.getParameter("Authorization");
            } else {
                //其他请求都是从请求头中获取token
                token = request.getHeader("Authorization");
            }

            if (!StringUtils.hasText(token)) {
                //token验证未通过的统一结果类
                R result = R.FAIL(CodeEnum.TOKEN_IS_EMPTY);
                //把R对象转成json
                String resultJSON = JSONUtils.toJSON(result);
                //把R以json返回给前端
                ResponseUtils.write(response, resultJSON);
                return;
            }

            //验证token有没有被篡改过
            if (!JWTUtils.verifyJWT(token)) {
                //token验证未通过统一结果类
                R result = R.FAIL(CodeEnum.TOKEN_IS_ERROR);

                //把R对象转成json
                String resultJSON = JSONUtils.toJSON(result);

                //把R以json返回给前端
                ResponseUtils.write(response, resultJSON);

                return;
            }

            TUser tUser = JWTUtils.parseUserFromJWT(token);
            String redisToken = (String) redisService.getValue(Constants.REDIS_JWT_KEY + tUser.getId());

            //验证token非空
            if (!StringUtils.hasText(redisToken)) {
                //token验证未通过统一结果类
                R result = R.FAIL(CodeEnum.TOKEN_IS_EXPIRED);

                //把R对象转成json
                String resultJSON = JSONUtils.toJSON(result);

                //把R以json返回给前端
                ResponseUtils.write(response, resultJSON);

                return;
            }

            //验证token是否与redis中的一致
            if (!token.equals(redisToken)) {
                //token验证未通过的统一结果类
                R result = R.FAIL(CodeEnum.TOKEN_IS_NONE_MATCH);

                //把R对象转成json
                String resultJSON = JSONUtils.toJSON(result);

                //把R以json返回给前端
                ResponseUtils.write(response, resultJSON);
                return;
            }

            //jwt验证通过了,那么在spring security的上下文环境中要设置一下,设置当前这个人是登录过的,你后续不要再拦截他了
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(tUser, tUser.getLoginPwd(), tUser.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);

            //刷新一下token(异步处理,new一个线程去执行)
            /*new Thread(() -> {
                //刷新token
                String rememberMe = request.getHeader("rememberMe");
                if (Boolean.parseBoolean(rememberMe)) {
                    redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.EXPIRE_TIME, TimeUnit.SECONDS);
                } else {
                    redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
                }
            }).start();*/

            //异步处理(更好的方式,使用线程池去执行)
            threadPoolTaskExecutor.execute(() -> {
                //刷新token
                String rememberMe = request.getHeader("rememberMe");
                if (Boolean.parseBoolean(rememberMe)) {
                    redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.EXPIRE_TIME, TimeUnit.SECONDS);
                } else {
                    redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
                }
            });

            //验证jwt通过了 ,让Filter链继续执行,也就是继续执行下一个Filter
            filterChain.doFilter(request, response);
        }
    }
}

4. token续期

现在一个问题是

token一旦发布, 是7天/30分钟 .

原来没有续期时 , token只能获得与提前删除.

然而一个现实需求是

假如用户获得是30分钟的token. 在这三十分钟内, 只要用户有任何操作, 我们应当自动刷新token到30分钟 , 否则假如不管用户怎么操作, token都固定30分钟刷新 , 这显然不符合逻辑

目标为, 任何除登出的用户登录都会重置token过期时间

然后是代码放在哪里的问题

我们知道, 即使登录过后 , 我们在每一次操作时仍会用TokenVerifyFilter检查token的有效期.

因此直接把续期代码放到该类中即可, 每次有操作->执行TokenVerifyFilter检查token有效期->有效则续期token

在TokenVerifyFilter中新增刷新代码文章来源地址https://www.toymoban.com/news/detail-834319.html

//刷新一下token(异步处理,new一个线程去执行)
//没必要因为续期token而阻塞整个进程, 毕竟此时已经校验过了不影响本次执行
            new Thread(() -> {
                //刷新token
                String rememberMe = request.getHeader("rememberMe");
                if (Boolean.parseBoolean(rememberMe)) {
                    redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.EXPIRE_TIME, TimeUnit.SECONDS);
                } else {
                    redisService.expire(Constants.REDIS_JWT_KEY + tUser.getId(), Constants.DEFAULT_EXPIRE_TIME, TimeUnit.SECONDS);
                }
            }).start();

到了这里,关于04 动力云客之登录后获取用户信息+JWT存进Redis+Filter验证Token + token续期的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 小程序获取手机号和用户信息一键登录

    1、一建获取用户信息 以前可以通过 button open-type=\\\"getUserInfo\\\" 一键登录/button 来实现一建获取用户信息; 2021年4月28日24时后发布的小程序新版本,无法通过wx.getUserInfo与获取用户个人信息(头像、昵称、性别与地区) 新增 getUserProfile 接口(基础库2.10.4版本开始支持),可获取

    2024年02月09日
    浏览(53)
  • 微信小程序登录,解决无法获取用户信息方法

    在上面的代码中,首先定义了应用的AppID和AppSecret。然后,实现了获取微信用户access_token的方法getAccessToken()和获取微信用户信息的方法getUserInfo()。其中,getAccessToken()方法需要传入微信小程序登录时获取的code,会返回一个包含access_token和openid的Map;getUserInfo()方法需要传入用户

    2024年02月11日
    浏览(66)
  • uniapp在小程序登录,获取用户信息,获取手机号等流程

    uniapp小程序的授权描述 1、微信小程序通过uni.login()方法可以获取到微信提供的code 2、通过登录获取的code码可以以获取用户唯一标识openid以及会话密钥sessionkey用于解密获取手机的加密信息 3、通过微信提供的获取微信手机号的方法getphonenumber拿到encryptedData iv两个字段的内容

    2024年02月12日
    浏览(49)
  • Unity微信小游戏登录授权获取用户信息

    最近需要在接微信获取用户信息的功能,在小游戏官方API中翻找资料。不得不说官方接口很多、很全,但是真的很乱而且部分遗漏。 对于不明所以的人来说,真的非常不友好。文档一堆堆的罗列下来,有些也不知道要怎么组合使用。 文档下有不少留言也是“骂骂咧咧”想必

    2024年02月02日
    浏览(58)
  • java对接微信小程序(登录&获取用户信息)

    用户通过小程序登录,进入到平台系统,进行各功能操作; 解决方案: 首先通过对接小程序,用户通过小程序登录及授权获取用户信息,后端调用接口获取微信用户信息,进行保存到数据库,然后返回token给前端(实际在这里相当于用户的一个注册及登录),前端使用该to

    2024年02月15日
    浏览(45)
  • uniapp获取用户信息(登录及个人中心页面的实现)

    因为在微信小程序中wx.getuserInfo已经失效,所以我们在uniapp中也应该使用wx.getUserProfile来获取用户信息 页面的逻辑 一上来加载个人中心页,当用户点击未登录三个字时跳转登录页 登录页点击微信登录应该跳出授权弹窗获取用户的授权信息(使用wx.getUserProfile) 当用户点击同意

    2024年02月11日
    浏览(53)
  • 支付宝小程序:获取用户信息和手机号快捷登录

    随着支付宝小程序以其便捷性和安全性受到了广大开发者和用户的青睐。在uni-app框架下开发支付宝小程序时,如何安全、合规地获取用户信息和手机号成为了开发者必须面对的问题。 1.在开始之前,确保你已经在支付宝开放平台上注册并创建应用,获取到应用的AppID; 2.小程

    2024年04月28日
    浏览(47)
  • 【微信小程序】使用微信API获取用户信息实现登录

    实现步骤 1. 通过定义button按钮点击事件,点击登录后触发事件 2. 通过 wx.getUserProfile API 调用用户信息 3. 获取成功就将数据存储到本地存储中,使用 wx.setStorageSync(\\\'user\\\', user) 4. 存储完获取本地数据 wx.getStorageSync(\\\'user\\\') 5. 页面渲染数据 实现登录 wxml页面 js页面  wxss页面

    2024年02月05日
    浏览(51)
  • 登录用户信息获取 网关+拦截器+feign请求添加请求头

    给所有请求添加用户身份 网关已经给所有请求添加了用户身份,也就是authorization头信息。   创建ThreadLocal工具类 : 创建拦截器:  将拦截器注册到SpringMvc,让它生效:  将以上代码(拦截器,config,utils) 放到哪个微服务中,哪个微服务/**路径就会有拦截功能 没有用户信息的请求将会

    2024年02月09日
    浏览(44)
  • java实现小程序获取微信登录,用户信息,手机号,头像

    在上面的代码中,首先定义了应用的AppID和AppSecret。然后,实现了获取微信用户access_token的方法getAccessToken()和获取微信用户信息的方法getUserInfo()。其中,getAccessToken()方法需要传入微信小程序登录时获取的code,会返回一个包含access_token和openid的Map;getUserInfo()方法需要传入用户

    2024年02月12日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包