【Springboot/redis】SpringBoot程序使用redis作为token管理器进行鉴权

【为何需要鉴权】

判断用户在session中是否存在即为鉴权,web程序不需要额外的鉴权器,httpSession即是;

但前后端分离后,前后端没有session进行交互,故需要设计一个类似session的鉴权器。

【鉴权器的设计理念】

用户登录成功后,往鉴权器中放置用户名(或id)和uuid的键值对,并设定超时时间;

用户请求服务时,取他header中的token,去查鉴权器中有否这个token,有则返回真实服务;

用户退出时,删除用户名(或id)和uuid的键值对;

超时时间一到,删除用户名(或id)和uuid的键值对;

【鉴权器的实现选型】

redis里的字符串即键值对模式,且自带键超时时间设置,速度也快,非常适合做鉴权器。

【鉴权器的接口定义】

package com.hy.token;

public interface TokenMng {
    public boolean join(String uname,String token);
    public boolean release(String uname);
    public String getToken(String uname);
    public boolean hasToken(String token);
}

【鉴权器的实现】

复制代码
package com.hy.token;

import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import java.util.Iterator;
import java.util.Set;

@Primary
@Component
public class RedisTokenMng implements TokenMng{
    @Override
    public boolean join(String uname, String token) {
        try {
            Jedis jedis = new Jedis("192.168.32.129");
            jedis.auth("123456");

            jedis.set(uname, token,"NX","EX",30);// 设置键值对并设定超时时间30秒

            return true;
        }catch(Exception ex){
            ex.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean release(String uname) {
        try {
            Jedis jedis = new Jedis("192.168.32.129");
            jedis.auth("123456");

            jedis.del(uname);

            return true;
        }catch(Exception ex){
            ex.printStackTrace();
            return false;
        }
    }

    @Override
    public String getToken(String uname) {
        try {
            Jedis jedis = new Jedis("192.168.32.129");
            jedis.auth("123456");

            return jedis.get(uname);
        }catch(Exception ex){
            ex.printStackTrace();
            return null;
        }
    }

    @Override
    public boolean hasToken(String token) {
        try {
            Jedis jedis = new Jedis("192.168.32.129");
            jedis.auth("123456");

            Set<String> keys = jedis.keys("*");  // 遍历所有键
            Iterator<String> it=keys.iterator() ;
            while(it.hasNext()){
                String key = it.next();
                String value=jedis.get(key);

                if(token.equals(value)){
                    return true;
                }
            }

            return false;
        }catch(Exception ex){
            ex.printStackTrace();
            return false;
        }
    }
}
复制代码

【用户登录时鉴权器的处理】

复制代码
package com.hy.ctrl;

import com.hy.token.TokenMng;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserRestCtrl {
    @Autowired
    private TokenMng tkMng;

    @GetMapping("/login")
    public String login(String uname,String pswd){
        if("ufo".equals(uname) && "1234".equals(pswd)){

            // join token
            String token=java.util.UUID.randomUUID().toString();
            tkMng.join(uname,token);

            // alert msg
            String msg=String.format("User:%s logged in,token=%s",uname,token);
            return msg;
        }else{
            return "Error username/password";
        }
    }

    public String logout(String uname){
        tkMng.release(uname);

        // alert msg
        String msg=String.format("User:%s logged out,token cleared",uname);
        return msg;
    }
}
复制代码

【拦截器中鉴权器对用户token的检验】

复制代码
package com.hy;

import com.hy.token.TokenMng;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

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

@Component
public class Interceptor implements HandlerInterceptor {
    @Autowired
    private TokenMng tkMng;

    @Override
    public boolean preHandle(HttpServletRequest rqst, HttpServletResponse rsps,Object handler) throws Exception{
        String rqstPath= rqst.getContextPath();
        System.out.println("Request comming,path="+rqstPath);

        // ordinary settings
        rsps.setCharacterEncoding("UTF-8");
        rsps.setContentType("text/html;charset=utf-8");
        rsps.addHeader("Access-Control-Allow-Origin","*");

        // token verify
        String token=rqst.getHeader("token");

        if(StringUtils.hasText(token)==false){
            rsps.getWriter().print("You have to login to get real response!");
            return false;
        }

        if(tkMng.hasToken(token)==false){
            String msg=String.format("Your token:%s does not exist.",token);
            rsps.getWriter().print(msg);
            return false;
        }

        return true;
    }
}
复制代码

【代表真实服务的控制器】

复制代码
package com.hy.ctrl;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestRestCtrl {
    @GetMapping(value="/sayhello")
    public String sayHello(){
        return "Hello world!";
    }
}
复制代码

【检验】

用户登录错误时:

这时请求sayhello

登录正确时:

url:http://localhost:8080/login?uname=ufo&&pswd=1234

resposne:User:ufo logged in,token=824d08e4-6193-41cb-8361-2dedbd83e54c

把824d08e4-6193-41cb-8361-2dedbd83e54c拷贝到postman的header里,再请求时:

等过了30秒超时时间,再次用postman请求:

【参考文档】:

SpringBoot使用token简单鉴权 

 【源码下载】:

https://files.cnblogs.com/files/heyang78/RedisTokenTest220307am.rar?t=1646625444

END

posted @   逆火狂飙  阅读(597)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
历史上的今天:
2020-02-22 求学生单科流水表中单科最近/最新的考试成绩表的三种方案(结果集鉴别篇)
2020-02-22 更新一张一千六百万大表字段值 附加一段文本和设置统一文本两方案耗时的比较
2020-02-22 『转载-保持学习的空杯心态』工作中如何做好技术积累
生当作人杰 死亦为鬼雄 至今思项羽 不肯过江东
点击右上角即可分享
微信分享提示