SpringBoot集成JWT實現許可權認證

  • 2019 年 11 月 8 日
  • 筆記

上一篇文章《一分鐘帶你了解JWT認證!》介紹了JWT的組成和認證原理,本文將介紹下SpringBoot整合JWT實現認證的過程,帶你更深入的了解下JWT。

一、JWT認證流程

JWT認證流程

認證流程如下:

  1. 用戶使用帳號和密碼發出post請求;
  2. 伺服器使用私鑰創建一個jwt;
  3. 伺服器返回這個jwt給瀏覽器;
  4. 瀏覽器將該jwt串在請求頭中像伺服器發送請求;
  5. 伺服器驗證該jwt;
  6. 返迴響應的資源給瀏覽器。

二、SpringBoot整合JWT

新建一個spring boot項目spring-boot-jwt,按照下面步驟操作。

1.pom.xml引入jar包

<!-- 引入jwt-->  <dependency>      <groupId>com.auth0</groupId>      <artifactId>java-jwt</artifactId>      <version>3.8.2</version>  </dependency>

2.新建Jwt工具類

Jwt工具類進行token的生成和認證,工具類程式碼如下:

/**   * @description: Jwt工具類,生成JWT和認證   * @author: Java碎碎念   */  public class JwtUtil {        private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);      /**       * 密鑰       */      private static final String SECRET = "my_secret";        /**       * 過期時間       **/      private static final long EXPIRATION = 1800L;//單位為秒        /**       * 生成用戶token,設置token超時時間       */      public static String createToken(User user) {          //過期時間          Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000);          Map<String, Object> map = new HashMap<>();          map.put("alg", "HS256");          map.put("typ", "JWT");          String token = JWT.create()                  .withHeader(map)// 添加頭部                  //可以將基本資訊放到claims中                  .withClaim("id", user.getId())//userId                  .withClaim("userName", user.getUserName())//userName                  .withClaim("name", user.getName())//name                  .withExpiresAt(expireDate) //超時設置,設置過期的日期                  .withIssuedAt(new Date()) //簽發時間                  .sign(Algorithm.HMAC256(SECRET)); //SECRET加密          return token;      }        /**       * 校驗token並解析token       */      public static Map<String, Claim> verifyToken(String token) {          DecodedJWT jwt = null;          try {              JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();              jwt = verifier.verify(token);          } catch (Exception e) {              logger.error(e.getMessage());              logger.error("token解碼異常");              //解碼異常則拋出異常              return null;          }          return jwt.getClaims();      }    }

3.添加JWT過濾器

JWT過濾器中進行token的校驗和判斷,,token不合法直接返回,合法則解密數據並把數據放到request中供後續使用。

為了使過濾器生效,需要在啟動類添加註解@ServletComponentScan(basePackages = "com.example.springbootjwt.filter")。

JWT過濾器程式碼如下:

/**   * JWT過濾器,攔截 /secure的請求   */  @Slf4j  @WebFilter(filterName = "JwtFilter", urlPatterns = "/secure/*")  public class JwtFilter implements Filter {      @Override      public void init(FilterConfig filterConfig) throws ServletException {      }        @Override      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {          final HttpServletRequest request = (HttpServletRequest) req;          final HttpServletResponse response = (HttpServletResponse) res;            response.setCharacterEncoding("UTF-8");          //獲取 header里的token          final String token = request.getHeader("authorization");            if ("OPTIONS".equals(request.getMethod())) {              response.setStatus(HttpServletResponse.SC_OK);              chain.doFilter(request, response);          }          // Except OPTIONS, other request should be checked by JWT          else {                if (token == null) {                  response.getWriter().write("沒有token!");                  return;              }                Map<String, Claim> userData = JwtUtil.verifyToken(token);              if (userData == null) {                  response.getWriter().write("token不合法!");                  return;              }              Integer id = userData.get("id").asInt();              String name = userData.get("name").asString();              String userName = userData.get("userName").asString();              //攔截器 拿到用戶資訊,放到request中              request.setAttribute("id", id);              request.setAttribute("name", name);              request.setAttribute("userName", userName);              chain.doFilter(req, res);          }      }        @Override      public void destroy() {      }  }

4.添加登錄Controller

登錄Controller進行登錄操作,登錄成功後生產token並返回。

登錄Controller程式碼如下:

/**   * 登錄Controller   */  @Slf4j  @RestController  public class LoginController {        static Map<Integer, User> userMap = new HashMap<>();        static {          //模擬資料庫          User user1 = new User(1, "zhangsan", "張三", "123456");          userMap.put(1, user1);          User user2 = new User(2, "lisi", "李四", "123123");          userMap.put(2, user2);      }        /**       * 模擬用戶 登錄       */      @RequestMapping("/login")      public String login(User user) {          for (User dbUser : userMap.values()) {              if (dbUser.getUserName().equals(user.getUserName()) && dbUser.getPassword().equals(user.getPassword())) {                  log.info("登錄成功!生成token!");                  String token = JwtUtil.createToken(dbUser);                  return token;              }          }          return "";      }  }

5.添加SecureController

SecureController中的請求會被JWT過濾器攔截,合法後才能訪問。

SecureController程式碼如下:

/**   * 需要登錄後才能訪問   */  @Slf4j  @RestController  public class SecureController {        /**       * 查詢 用戶資訊,登錄後才能訪問       */      @RequestMapping("/secure/getUserInfo")      public String login(HttpServletRequest request) {          Integer id = (Integer) request.getAttribute("id");          String name = request.getAttribute("name").toString();          String userName = request.getAttribute("userName").toString();          return "當前用戶資訊id=" + id + ",name=" + name + ",userName=" + userName;      }  }

三、測試

測試分兩步,首先訪問登錄介面,登錄成功後獲取token,然後拿著token在訪問查詢用戶資訊介面。

1.訪問登錄介面

打開PostMan,訪問http://localhost:8080/login?userName=zhangsan&password=123456,登錄成功後介面返回token,請求成功截圖如下:

獲取token截圖

2.訪問用戶資訊介面

打開PostMan,訪問http://localhost:8080/secure/getUserInfo,header里需要攜帶token,請求成功截圖如下:

請求用戶資訊截圖

到此SpringBoot整合JWT的功能已經全部實現,有問題歡迎留言溝通哦!

完整源碼地址: https://github.com/suisui2019/springboot-study

推薦閱讀

1.一分鐘帶你了解JWT認證!
2.SpringBoot中如何優雅的讀取yml配置文件?
3.SpringBoot中如何靈活的實現介面數據的加解密功能?
4.SpringBoot中神奇的@Enable*註解?
5.Java中Integer.parseInt和Integer.valueOf,你還傻傻分不清嗎?


限時領取免費Java相關資料,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高並發分散式、大數據、機器學習等技術。
關注下方公眾號即可免費領取:

Java碎碎念公眾號