• 【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

  • 相关阅读:
    Delphi XE5 图解为Android应用制作签名
    Delphi事件的广播 转
    Delphi XE6 Android拨号函数
    Firemonkey里触发home按键被按下的事件
    如何处理App的Application的事件
    SQL SERVER存储过程中使用事务与捕获异常
    关于蓝牙打印机的开发
    图标大全网站
    提高Android和iOS调试编译速度
    百万级数据查询优化(数据库)
  • 原文地址:https://www.cnblogs.com/heyang78/p/15924977.html
Copyright © 2020-2023  润新知