1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        SpringBoot + Redis實現(xiàn)token權(quán)限認證(附源碼)

        共 6801字,需瀏覽 14分鐘

         ·

        2022-01-02 13:14

        點擊上方藍色字體,選擇“標星公眾號”

        優(yōu)質(zhì)文章,第一時間送達

        一、引言

        登陸權(quán)限控制是每個系統(tǒng)都應(yīng)必備的功能,實現(xiàn)方法也有好多種。下面使用Token認證來實現(xiàn)系統(tǒng)的權(quán)限訪問。

        功能描述:

        用戶登錄成功后,后臺返回一個token給調(diào)用者,同時自定義一個@AuthToken注解,被該注解標注的API請求都需要進行token效驗,效驗通過才可以正常訪問,實現(xiàn)接口級的鑒權(quán)控制。同時token具有生命周期,在用戶持續(xù)一段時間不進行操作的話,token則會過期,用戶一直操作的話,則不會過期。

        二、環(huán)境

        • SpringBoot
        • Redis(Docke中鏡像)
        • MySQL(Docker中鏡像)

        三、流程分析

        1、流程分析

        (1)、客戶端登錄,輸入用戶名和密碼,后臺進行驗證,如果驗證失敗則返回登錄失敗的提示。如果驗證成功,則生成 token 然后將 username 和 token 雙向綁定 (可以根據(jù) username 取出 token 也可以根據(jù) token 取出username)存入redis,同時使用 token+username 作為key把當前時間戳也存入redis。并且給它們都設(shè)置過期時間。

        (2)、每次請求接口都會走攔截器,如果該接口標注了@AuthToken注解,則要檢查客戶端傳過來的Authorization字段,獲取 token。由于 token 與 username 雙向綁定,可以通過獲取的 token 來嘗試從 redis 中獲取 username,如果可以獲取則說明 token 正確,反之,說明錯誤,返回鑒權(quán)失敗。

        (3)、token可以根據(jù)用戶使用的情況來動態(tài)的調(diào)整自己過期時間。在生成 token 的同時也往 redis 里面存入了創(chuàng)建 token 時的時間戳,每次請求被攔截器攔截 token 驗證成功之后,將當前時間與存在 redis 里面的 token 生成時刻的時間戳進行比較,當當前時間的距離創(chuàng)建時間快要到達設(shè)置的redis過期時間的話,就重新設(shè)置token過期時間,將過期時間延長。如果用戶在設(shè)置的 redis 過期時間的時間長度內(nèi)沒有進行任何操作(沒有發(fā)請求),則token會在redis中過期。

        四、具體代碼實現(xiàn)

        1、自定義注解

        @Target({ElementType.METHOD,?ElementType.TYPE})
        @Retention(RetentionPolicy.RUNTIME)
        public?@interface?AuthToken?{

        }

        2、登陸控制器

        @RestController
        public?class?welcome?{

        ????Logger?logger?=?LoggerFactory.getLogger(welcome.class);

        ????@Autowired
        ????Md5TokenGenerator?tokenGenerator;

        ????@Autowired
        ????UserMapper?userMapper;

        ????@GetMapping("/welcome")
        ????public?String?welcome(){

        ????????return?"welcome?token?authentication";
        ????}

        ????@RequestMapping(value?=?"/login",?method?=?RequestMethod.GET)
        ????public?ResponseTemplate?login(String?username,?String?password)?{

        ????????logger.info("username:"+username+"??????password:"+password);
        ????????User?user?=?userMapper.getUser(username,password);
        ????????logger.info("user:"+user);

        ????????JSONObject?result?=?new?JSONObject();
        ????????if?(user?!=?null)?{

        ????????????Jedis?jedis?=?new?Jedis("192.168.1.106",?6379);
        ????????????String?token?=?tokenGenerator.generate(username,?password);
        ????????????jedis.set(username,?token);
        ????????????//設(shè)置key生存時間,當key過期時,它會被自動刪除,時間是秒
        ????????????jedis.expire(username,?ConstantKit.TOKEN_EXPIRE_TIME);
        ????????????jedis.set(token,?username);
        ????????????jedis.expire(token,?ConstantKit.TOKEN_EXPIRE_TIME);
        ????????????Long?currentTime?=?System.currentTimeMillis();
        ????????????jedis.set(token?+?username,?currentTime.toString());

        ????????????//用完關(guān)閉
        ????????????jedis.close();

        ????????????result.put("status",?"登錄成功");
        ????????????result.put("token",?token);
        ????????}?else?{
        ????????????result.put("status",?"登錄失敗");
        ????????}

        ????????return?ResponseTemplate.builder()
        ????????????????.code(200)
        ????????????????.message("登錄成功")
        ????????????????.data(result)
        ????????????????.build();

        ????}

        ?//測試權(quán)限訪問
        ????@RequestMapping(value?=?"test",?method?=?RequestMethod.GET)
        ????@AuthToken
        ????public?ResponseTemplate?test()?{

        ????????logger.info("已進入test路徑");

        ????????return?ResponseTemplate.builder()
        ????????????????.code(200)
        ????????????????.message("Success")
        ????????????????.data("test?url")
        ????????????????.build();
        ????}

        }

        3、攔截器

        @Slf4j
        public?class?AuthorizationInterceptor?implements?HandlerInterceptor?{

        ????//存放鑒權(quán)信息的Header名稱,默認是Authorization
        ????private?String?httpHeaderName?=?"Authorization";

        ????//鑒權(quán)失敗后返回的錯誤信息,默認為401?unauthorized
        ????private?String?unauthorizedErrorMessage?=?"401?unauthorized";

        ????//鑒權(quán)失敗后返回的HTTP錯誤碼,默認為401
        ????private?int?unauthorizedErrorCode?=?HttpServletResponse.SC_UNAUTHORIZED;

        ?//存放登錄用戶模型Key的Request?Key
        ????public?static?final?String?REQUEST_CURRENT_KEY?=?"REQUEST_CURRENT_KEY";

        ????@Override
        ????public?boolean?preHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler)?throws?Exception?{
        ????????if?(!(handler?instanceof?HandlerMethod))?{
        ????????????return?true;
        ????????}
        ????????HandlerMethod?handlerMethod?=?(HandlerMethod)?handler;
        ????????Method?method?=?handlerMethod.getMethod();

        ????????//?如果打上了AuthToken注解則需要驗證token
        ????????if?(method.getAnnotation(AuthToken.class)?!=?null?||?handlerMethod.getBeanType().getAnnotation(AuthToken.class)?!=?null)?{

        ????????????String?token?=?request.getParameter(httpHeaderName);
        ????????????log.info("Get?token?from?request?is?{}?",?token);
        ????????????String?username?=?"";
        ????????????Jedis?jedis?=?new?Jedis("192.168.1.106",?6379);
        ????????????if?(token?!=?null?&&?token.length()?!=?0)?{
        ????????????????username?=?jedis.get(token);
        ????????????????log.info("Get?username?from?Redis?is?{}",?username);
        ????????????}
        ????????????if?(username?!=?null?&&?!username.trim().equals(""))?{
        ????????????????Long?tokeBirthTime?=?Long.valueOf(jedis.get(token?+?username));
        ????????????????log.info("token?Birth?time?is:?{}",?tokeBirthTime);
        ????????????????Long?diff?=?System.currentTimeMillis()?-?tokeBirthTime;
        ????????????????log.info("token?is?exist?:?{}?ms",?diff);
        ????????????????if?(diff?>?ConstantKit.TOKEN_RESET_TIME)?{
        ????????????????????jedis.expire(username,?ConstantKit.TOKEN_EXPIRE_TIME);
        ????????????????????jedis.expire(token,?ConstantKit.TOKEN_EXPIRE_TIME);
        ????????????????????log.info("Reset?expire?time?success!");
        ????????????????????Long?newBirthTime?=?System.currentTimeMillis();
        ????????????????????jedis.set(token?+?username,?newBirthTime.toString());
        ????????????????}

        ????????????????//用完關(guān)閉
        ????????????????jedis.close();
        ????????????????request.setAttribute(REQUEST_CURRENT_KEY,?username);
        ????????????????return?true;
        ????????????}?else?{
        ????????????????JSONObject?jsonObject?=?new?JSONObject();

        ????????????????PrintWriter?out?=?null;
        ????????????????try?{
        ????????????????????response.setStatus(unauthorizedErrorCode);
        ????????????????????response.setContentType(MediaType.APPLICATION_JSON_VALUE);

        ????????????????????jsonObject.put("code",?((HttpServletResponse)?response).getStatus());
        ????????????????????jsonObject.put("message",?HttpStatus.UNAUTHORIZED);
        ????????????????????out?=?response.getWriter();
        ????????????????????out.println(jsonObject);

        ????????????????????return?false;
        ????????????????}?catch?(Exception?e)?{
        ????????????????????e.printStackTrace();
        ????????????????}?finally?{
        ????????????????????if?(null?!=?out)?{
        ????????????????????????out.flush();
        ????????????????????????out.close();
        ????????????????????}
        ????????????????}

        ????????????}

        ????????}

        ????????request.setAttribute(REQUEST_CURRENT_KEY,?null);

        ????????return?true;
        ????}

        ????@Override
        ????public?void?postHandle(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler,?ModelAndView?modelAndView)?throws?Exception?{

        ????}

        ????@Override
        ????public?void?afterCompletion(HttpServletRequest?request,?HttpServletResponse?response,?Object?handler,?Exception?ex)?throws?Exception?{

        ????}
        }

        4、測試結(jié)果

        五、小結(jié)

        登陸權(quán)限控制,實際上利用的就是攔截器的攔截功能。因為每一次請求都要通過攔截器,只有攔截器驗證通過了,才能訪問想要的請求路徑,所以在攔截器中做校驗Token校驗。想要代碼,可以去GitHub上查看。

        https://github.com/Hofanking/token-authentication.git

        攔截器介紹,可以參考:

        https://blog.csdn.net/zxd1435513775/article/details/80556034

        來源:blog.csdn.net/zxd1435513775/article/details/86555130


        瀏覽 26
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            少妇宾馆露脸高潮不断 | 美女高潮喷水视频 | 欧美成人性爱在线 | 亚洲少妇激情 | 台湾久久夕三极片 | 日本一级婬片A片AAA片口技 | 国产精品片www48888 | 孕妇被医生粗大挺进 | 久久夜色精品国产亚洲AV卜 | 人人插97 |