前后端分離JWT Token登錄遗座,后端Springboot代碼

參考自文章

springboot+vue實現(xiàn)token驗證

Springboot+Vue前后端分離實現(xiàn)token登錄驗證和狀態(tài)保存

阮一峰——JSON Web Token 入門教程


1. 后端Springboot

目錄結構

image

1.1 攔截器LoginInteceptor類 (com.chinasoft.config)包

package com.chinasoft.config;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import org.springframework.web.method.HandlerMethod;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSONObject;

import com.auth0.jwt.JWT;

import com.auth0.jwt.JWTVerifier;

import com.auth0.jwt.algorithms.Algorithm;

import com.auth0.jwt.exceptions.JWTDecodeException;

import com.auth0.jwt.exceptions.JWTVerificationException;

import com.auth0.jwt.interfaces.DecodedJWT;

import com.chinasoft.annotation.PassToken;

import com.chinasoft.annotation.UserLoginToken;

import com.chinasoft.pojo.User;

import com.chinasoft.service.UserService;

/**

* 未登錄攔截器

*/

@Component

public class LoginInterceptor implements HandlerInterceptor {

    @Autowired

    private UserService userService;

    @Override

    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {

    String token = httpServletRequest.getHeader("Authorization");// 從 http 請求頭中取出 token

        // 如果不是映射到方法直接通過

        if (!(object instanceof HandlerMethod)) {

            return true;

        }

        HandlerMethod handlerMethod = (HandlerMethod) object;

        Method method = handlerMethod.getMethod();

        //檢查是否有passtoken注釋,有則跳過認證

        if (method.isAnnotationPresent(PassToken.class)) {

            PassToken passToken = method.getAnnotation(PassToken.class);

            if (passToken.required()) {

                return true;

            }

        }

        //檢查有沒有需要用戶權限的注解

        if (method.isAnnotationPresent(UserLoginToken.class)) {

            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);

            if (userLoginToken.required()) {

                // 執(zhí)行認證俄删,無token

                if (token == null) {

                httpServletResponse.setCharacterEncoding("UTF-8");

                httpServletResponse.setContentType("application/json; charset=utf-8");

            try{

                  JSONObject json = new JSONObject();

                  json.put("msg","token verify fail");

                  json.put("code","50000");

                  httpServletResponse.getWriter().append(json.toJSONString());

                }catch (Exception e){

                  e.printStackTrace();

                  httpServletResponse.sendError(500);

                  return false;

                }

            return false;

                }

                // 獲取 token 中的 user id

                String userId = null;

                try {

                    userId = JWT.decode(token).getAudience().get(0);

                } catch (JWTDecodeException j) {

                try{

                    JSONObject json = new JSONObject();

                    json.put("msg","usertoken verify fail");

                    json.put("code","401");

                    httpServletResponse.getWriter().append(json.toJSONString());

                    System.out.println("身份驗證錯誤");

                  }catch (Exception e){

                    e.printStackTrace();

                    httpServletResponse.sendError(401);

                    return false;

                  }

                return false;

                }

                // 獲取用戶

                int id = Integer.parseInt(userId);

                User user = userService.findOneUserById(id);

                if (user == null) {

                // 返回錯誤信息

                try{

                    JSONObject json = new JSONObject();

                    json.put("msg","no user");

                    json.put("code","50000");

                    httpServletResponse.getWriter().append(json.toJSONString());

                    System.out.println("不存在該用戶");

                  }catch (Exception e){

                    e.printStackTrace();

                    httpServletResponse.sendError(500);

                    return false;

                  }

                return false;

                }

                // 驗證 token

                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();

                try {

                DecodedJWT jwt = jwtVerifier.verify(token);

                System.out.println("認證通過:");

                    System.out.println("過期時間:      " + jwt.getExpiresAt());

                } catch (JWTVerificationException e) {

                try{

                  JSONObject json = new JSONObject();

                  json.put("msg","token has expired");

                  json.put("code","401");

                  httpServletResponse.getWriter().append(json.toJSONString());

                  System.out.println("token已過期");

                }catch (Exception ex){

                  ex.printStackTrace();

                  httpServletResponse.sendError(401);

                  return false;

                }

                return false;

                }

                return true;

            }

        }

        return true;

    }

    @Override

    public void postHandle(HttpServletRequest httpServletRequest,

                          HttpServletResponse httpServletResponse,

                          Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override

    public void afterCompletion(HttpServletRequest httpServletRequest,

                                HttpServletResponse httpServletResponse,

                                Object o, Exception e) throws Exception {

    }
}

1.2 注冊攔截器LoginInteceptor(com.chinasoft.config包)

package com.chinasoft.config;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**

* 注冊攔截器

*/

@Configuration

public class WebConfigurer implements WebMvcConfigurer {

    @Autowired

    private LoginInterceptor loginHandlerInterceptor;

    @Override

    public void addInterceptors(InterceptorRegistry registry) {

    registry.addInterceptor(loginHandlerInterceptor)

        .addPathPatterns("/**");    // 攔截所有請求疗锐,通過判斷是否有 @LoginRequired 注解 決定是否需要登錄

    }

    @Bean

    public LoginInterceptor loginHandlerInterceptor() {

        return new LoginInterceptor();

    }

}

1.3 在TokenServiceImpl類中創(chuàng)建生成Token方法(包com.chinasoft.service.impl)


package com.chinasoft.service.impl;

import java.util.Date;

import org.springframework.stereotype.Service;

import com.auth0.jwt.JWT;

import com.auth0.jwt.algorithms.Algorithm;

import com.chinasoft.pojo.User;

import com.chinasoft.service.TokenService;

@Service

public class TokenServiceImpl implements TokenService{

@Override

public String getToken(User user) {

        Date start = new Date();

        long currentTime = System.currentTimeMillis() + 60* 60 * 1000;//一小時有效時間

        Date end = new Date(currentTime);

        String token = "";

        // 生成token愉豺,開始時間允乐,結束時間

        token = JWT.create().withAudience(user.getUid() + "").withIssuedAt(start).withExpiresAt(end)

                .sign(Algorithm.HMAC256(user.getPassword()));

        return token;

    }

}

1.4 跳過身份驗證的注解@PassToken 和需要身份驗證的注解 @UserLoginToken(包com.chinasoft.annotation)

PassToken

package com.chinasoft.annotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

* @author :ZWQ

* @version :1.0

* @date :2019/10/16 - 18:44

* @description :用來跳過驗證的PassToken

*/

@Target({ElementType.METHOD, ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

public @interface PassToken {

    boolean required() default true;

}

UserLoginToken

package com.chinasoft.annotation;

import java.lang.annotation.ElementType;

import java.lang.annotation.Retention;

import java.lang.annotation.RetentionPolicy;

import java.lang.annotation.Target;

/**

* @author :ZWQ

* @version :1.0

* @date :2019/10/16 - 18:46

* @description :需要登錄才能進行操作的注解UserLoginToken

*/

@Target({ElementType.METHOD, ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

public @interface UserLoginToken {

    boolean required() default true;

}

1.5 Controller中的login方法(包com.chinasoft.controller)

import com.chinasoft.service.TokenService;

import com.chinasoft.service.UserService;

@Controller

@RequestMapping("user")

@CrossOrigin

public class UserController {

@Autowired

UserService service;

@Autowired

    TokenService tokenService;

// 登錄(需要賬號密碼)

@RequestMapping("login")

@ResponseBody

public Map login(String name, String password, HttpServletRequest request, HttpServletResponse response) {

Map<String,Object> hm = new HashMap<>();    // 通過map返回token和user

User user = service.LoginUser(name, password);    // Service類中的方法驗證并獲取用戶

hm.put("user", user);

if (user == null) {

System.out.println("該用戶不存在");

return null;

} else {

System.out.println("登錄成功");

}

// 生成token并返回

String token = tokenService.getToken(user);

hm.put("token", token);

return hm;

}

}

2. 前端Vue代碼實現(xiàn)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末务甥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子喳篇,更是在濱河造成了極大的恐慌,老刑警劉巖态辛,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件麸澜,死亡現(xiàn)場離奇詭異,居然都是意外死亡奏黑,警方通過查閱死者的電腦和手機炊邦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熟史,“玉大人馁害,你說我怎么就攤上這事□迤ィ” “怎么了碘菜?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長限寞。 經(jīng)常有香客問我忍啸,道長,這世上最難降的妖魔是什么履植? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任计雌,我火速辦了婚禮,結果婚禮上玫霎,老公的妹妹穿的比我還像新娘凿滤。我一直安慰自己,他們只是感情好庶近,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布翁脆。 她就那樣靜靜地躺著,像睡著了一般拦盹。 火紅的嫁衣襯著肌膚如雪鹃祖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音恬口,去河邊找鬼校读。 笑死,一個胖子當著我的面吹牛祖能,可吹牛的內容都是我干的歉秫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼养铸,長吁一口氣:“原來是場噩夢啊……” “哼雁芙!你這毒婦竟也來了?” 一聲冷哼從身側響起钞螟,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤兔甘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后鳞滨,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洞焙,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年拯啦,在試婚紗的時候發(fā)現(xiàn)自己被綠了澡匪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡褒链,死狀恐怖唁情,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情甫匹,我是刑警寧澤甸鸟,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站赛惩,受9級特大地震影響哀墓,放射性物質發(fā)生泄漏。R本人自食惡果不足惜喷兼,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一篮绰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧季惯,春花似錦吠各、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至藕筋,卻和暖如春纵散,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工伍掀, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留掰茶,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓蜜笤,卻偏偏與公主長得像濒蒋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子把兔,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內容