1、关于JWT,参考:

  (1)10分钟了解JSON Web令牌(JWT)

  (2)认识JWT

  (3)基于jwt的token验证

2、JWT的JAVA实现

Java中对JWT的支持可以考虑使用JJWT开源库;JJWT实现了JWT, JWS, JWE 和 JWA RFC规范;下面将简单举例说明其使用:

2.1、生成Token码

  1. import javax.crypto.spec.SecretKeySpec;
  2. import javax.xml.bind.DatatypeConverter;
  3. import java.security.Key;
  4. import io.jsonwebtoken.*;
  5. import java.util.Date;
  6.  
  7. //Sample method to construct a JWT
  8. private String createJWT(String id, String issuer, String subject, long ttlMillis) {
  9.  
  10.   //The JWT signature algorithm we will be using to sign the token
  11.   SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
  12.  
  13.   long nowMillis = System.currentTimeMillis();
  14.   Date now = new Date(nowMillis);
  15.  
  16.   //We will sign our JWT with our ApiKey secret
  17.   byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(apiKey.getSecret());
  18.   Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
  19.  
  20.   //Let's set the JWT Claims
  21.   JwtBuilder builder = Jwts.builder().setId(id)
  22. .setIssuedAt(now)
  23. .setSubject(subject)
  24. .setIssuer(issuer)
  25. .signWith(signatureAlgorithm, signingKey);
  26.  
  27.   //if it has been specified, let's add the expiration
  28.   if (ttlMillis >= 0) {
  29.   long expMillis = nowMillis + ttlMillis;
  30.   Date exp = new Date(expMillis);
  31.   builder.setExpiration(exp);
  32.   }
  33.  
  34.   //Builds the JWT and serializes it to a compact, URL-safe string
  35.   return builder.compact();
  36. }

2.2、解码和验证Token码

  1. import javax.xml.bind.DatatypeConverter;
  2. import io.jsonwebtoken.Jwts;
  3. import io.jsonwebtoken.Claims;
  4.  
  5. //Sample method to validate and read the JWT
  6. private void parseJWT(String jwt) {
  7.   //This line will throw an exception if it is not a signed JWS (as expected)
  8.   Claims claims = Jwts.parser()
  9.   .setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret()))
  10.   .parseClaimsJws(jwt).getBody();
  11.   System.out.println("ID: " + claims.getId());
  12.   System.out.println("Subject: " + claims.getSubject());
  13.   System.out.println("Issuer: " + claims.getIssuer());
  14.   System.out.println("Expiration: " + claims.getExpiration());
  15. }

3、springboot + 注解 + 拦截器 + JWT 实现角色权限控制

  demo涉及的技术:springboot 2.1.5.RELEASE + 注解 + 拦截器 + JWT。

  demo功能:模拟角色权限控制。使用注解@Role标注的Controller方法,都需要进行token验证,判断token中role的值与@Role注解中role的值是否相同。

  

3.1、pom文件

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-web</artifactId>
  5. </dependency>
  6. <dependency>
  7. <groupId>io.jsonwebtoken</groupId>
  8. <artifactId>jjwt</artifactId>
  9. <version>0.7.0</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>com.alibaba</groupId>
  13. <artifactId>fastjson</artifactId>
  14. <version>1.2.47</version>
  15. </dependency>
  16. </dependencies>

3.2、token工具类JwtUtil

  1. package com.oy.util;
  2. import java.util.Date;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. import java.util.Map.Entry;
  6. import javax.servlet.http.HttpServletRequest;
  7. import org.junit.jupiter.api.Test;
  8. import com.oy.model.User;
  9. import io.jsonwebtoken.Jwts;
  10. import io.jsonwebtoken.SignatureAlgorithm;
  11.  
  12. public class JwtUtil {
  13. public static final long EXPIRATION_TIME = 3600_000; // 1 hour
  14. // public static final long EXPIRATION_TIME = 0; // for test
  15. public static final String SECRET = "secret007";
  16. public static final String TOKEN_PREFIX = "Bearer";
  17. public static final String HEADER_STRING = "Authorization";
  18.  
  19. // 生成token
  20. public static String generateToken(User user) {
  21. //you can put any data into the map
  22. HashMap<String, Object> map = new HashMap<>();
  23. map.put("name", user.getUsername());
  24. map.put("role", user.getRole());
  25.  
  26. String jwt = Jwts.builder()
  27. .setClaims(map) // 数据
  28. .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // 过期时间
  29. .signWith(SignatureAlgorithm.HS512, SECRET) // 算法,密钥
  30. .compact();
  31. return TOKEN_PREFIX + " " + jwt;
  32. }
  33.  
  34. // 验证和解析token
  35. public static Map<String, Object> validateTokenAndGetClaims(HttpServletRequest request) {
  36. String token = request.getHeader(HEADER_STRING); // Authorization
  37. if (token == null)
  38. throw new TokenValidationException("Missing token");
  39.  
  40. // Parse the token. Throw exception when token is invalid
  41. Map<String, Object> body = Jwts.parser()
  42. .setSigningKey(SECRET)
  43. .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
  44. .getBody();
  45. return body;
  46. }
  47.  
  48. @SuppressWarnings("serial")
  49. public static class TokenValidationException extends RuntimeException {
  50. public TokenValidationException(String msg) {
  51. super(msg);
  52. }
  53. }
  54.  
  55. // 测试生成jwt
  56. @Test
  57. public void test1() {
  58. UtilFunctions.log.info(generateToken(new User("admin", "", "admin")));
  59. // Bearer eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiYWRtaW4iLCJuYW1lIjoiYWRtaW4iLCJleHAiOjE1NjAwNzI1MTN9.YRG59eqP8nIoRNURPRYZWv3SAtss9YLOXjRsVmLmms7qMImq4MsERN0QuDbLGorgLCAbrIiSJjBY5_DaPJqP6Q
  60. }
  61.  
  62. // 测试验证和解析token
  63. // 测试使用错误的密钥: io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature.
  64. // JWT validity cannot be asserted and should not be trusted.
  65. @Test
  66. public void test2() {
  67. String token = "eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiYWRtaW4iLCJuYW1lIjoiYWRtaW4iLCJleHAiOjE1NjAwNzI1MTN9.YRG59eqP8nIoRNURPRYZWv3SAtss9YLOXjRsVmLmms7qMImq4MsERN0QuDbLGorgLCAbrIiSJjBY5_DaPJqP6Q";
  68. Map<String, Object> body = Jwts.parser()
  69. //.setSigningKey(SECRET)
  70. .setSigningKey("secret") // 测试使用错误的密钥
  71. .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
  72. .getBody();
  73.  
  74. for (Entry<String, Object> entry : body.entrySet()) {
  75. UtilFunctions.log.info("=== key:{}, value:{} ===", entry.getKey(), entry.getValue());
  76. // === key:role, value:admin ===
  77. // === key:name, value:admin ===
  78. // === key:exp, value:1560072513 ===
  79. }
  80. }
  81. }

3.3、注解@Role的定义

  1. package com.oy.filter;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6.  
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Target(ElementType.METHOD)
  9. public @interface Role {
  10. String role();
  11. }

3.4、拦截器RoleInterceptor

  1. package com.oy.filter;
  2. import java.util.Map;
  3. import javax.servlet.http.HttpServletRequest;
  4. import javax.servlet.http.HttpServletResponse;
  5. import org.springframework.web.method.HandlerMethod;
  6. import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
  7. import com.oy.util.AuthException;
  8. import com.oy.util.JwtUtil;
  9. import com.oy.util.UtilFunctions;
  10.  
  11. public class RoleInterceptor extends HandlerInterceptorAdapter {
  12.  
  13. @Override
  14. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
  15. throws Exception {
  16.  
  17. UtilFunctions.log.info("RoleInterceptor work...");
  18.  
  19. // target of request is method of controller
  20. if (handler instanceof HandlerMethod) {
  21. HandlerMethod handlerMethod = (HandlerMethod) handler;
  22. Role roleAnnotation = handlerMethod.getMethodAnnotation(Role.class);
  23.  
  24. if (roleAnnotation == null) { // method without @Role annotation
  25. UtilFunctions.log.info("RoleInterceptor over...");
  26. return true; // 放行
  27. } else { // method with @Role annotation
  28. // 验证token
  29. String role = null;
  30. try {
  31. Map<String, Object> claims = JwtUtil.validateTokenAndGetClaims(request);
  32. UtilFunctions.log.info("claims:{}", claims);
  33. role = String.valueOf(claims.get("role")); // 从token中取数据: role
  34. // roleAnnotation.role(): 获取注解中指定role
  35. UtilFunctions.log.info("=== role:{}, roleAnnotation.role:{} ===", role, roleAnnotation.role());
  36.  
  37. if (System.currentTimeMillis() / 1000L > (int) claims.get("exp")) {
  38. throw new AuthException("token 过期了...");
  39. }
  40. } catch (Exception e) {
  41. // response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
  42. // UtilFunctions.log.info(e.toString() + ". RoleInterceptor over...");
  43. // return false; // 拦截
  44. // token验证不通过,拦截
  45. throw new AuthException(e.getMessage());
  46. }
  47.  
  48. if (role == null || !role.equals(roleAnnotation.role())) {
  49. // response.setStatus(401);
  50. // UtilFunctions.log.info("RoleInterceptor over...");
  51. // return false; // 拦截
  52. throw new AuthException("a role of " + roleAnnotation.role() + " is needed, but you are " + role);
  53. }
  54. }
  55. }
  56.  
  57. UtilFunctions.log.info("RoleInterceptor over...");
  58. return true; // // 放行
  59. }
  60.  
  61. }

  在sprigboot中注册拦截器

  1. package com.oy;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.servlet.config.annotation.EnableWebMvc;
  4. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  5. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  6. import com.oy.filter.RoleInterceptor;
  7.  
  8. @EnableWebMvc
  9. @Configuration
  10. public class webConfig implements WebMvcConfigurer {
  11.  
  12. @Override
  13. public void addInterceptors(InterceptorRegistry registry) {
  14. // 拦截除登陆外的所有请求
  15. registry.addInterceptor(new RoleInterceptor())
  16. .addPathPatterns("/**").excludePathPatterns("/login");
  17. }
  18.  
  19. }

3.5、IndexController

  1. package com.oy.controller;
  2. import java.util.HashMap;
  3. import javax.servlet.http.HttpServletResponse;
  4. import org.springframework.http.HttpStatus;
  5. import org.springframework.http.ResponseEntity;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.PostMapping;
  8. import org.springframework.web.bind.annotation.RequestParam;
  9. import org.springframework.web.bind.annotation.RestController;
  10. import com.alibaba.fastjson.JSONObject;
  11. import com.oy.filter.Role;
  12. import com.oy.model.User;
  13. import com.oy.util.JwtUtil;
  14. import com.oy.util.Response;
  15. import com.oy.util.UtilFunctions;
  16.  
  17. @RestController
  18. public class IndexController {
  19.  
  20. @Role(role = "user")
  21. @GetMapping("/test1")
  22. public JSONObject test1() {
  23. return new Response("test role=user").toJson();
  24. }
  25.  
  26. @Role(role = "admin")
  27. @GetMapping("/test2")
  28. public JSONObject test2() {
  29. return new Response("test role=admin").toJson();
  30. }
  31.  
  32. // 登录方法不拦截
  33. @SuppressWarnings({ "serial", "rawtypes" })
  34. @PostMapping("/login")
  35. public Object login(HttpServletResponse response,
  36. @RequestParam(value = "username", required = true) String username,
  37. @RequestParam(value = "password", required = true) String password) throws Exception {
  38.  
  39. UtilFunctions.log.info("login info, username:{}, password:{}", username, password);
  40.  
  41. User user = isValidUsernameAndPassword(username, password);
  42. if (user != null) {
  43. String jwt = JwtUtil.generateToken(user);
  44. return new HashMap<String, String>() {
  45. {
  46. put("token", jwt);
  47. }
  48. };
  49. } else {
  50. //
  51. return new ResponseEntity(HttpStatus.UNAUTHORIZED);
  52. }
  53. }
  54.  
  55. private User isValidUsernameAndPassword(String username, String password) {
  56. if ("admin".equals(username) && "admin123".equals(password)
  57. || "user".equals(username) && "user123".equals(password)) {
  58. return new User("admin", "", "admin");
  59. }
  60. return null;
  61. }
  62. }

3.6、全局异常处理ExceptionHandlerController

  1. package com.oy.controller;
  2.  
  3. import org.springframework.http.HttpStatus;
  4. import org.springframework.web.bind.annotation.ControllerAdvice;
  5. import org.springframework.web.bind.annotation.ExceptionHandler;
  6. import org.springframework.web.bind.annotation.ResponseBody;
  7. import org.springframework.web.bind.annotation.ResponseStatus;
  8.  
  9. import com.alibaba.fastjson.JSONObject;
  10. import com.oy.util.AuthException;
  11.  
  12. @ControllerAdvice
  13. public class ExceptionHandlerController {
  14.  
  15. @ExceptionHandler(RuntimeException.class)
  16. @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  17. @ResponseBody
  18. public JSONObject runtimeExceptionHandler(RuntimeException ex) {
  19. JSONObject response = new JSONObject();
  20. response.put("message", ex.getMessage());
  21. return response;
  22. }
  23.  
  24. @ExceptionHandler(Exception.class)
  25. @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  26. @ResponseBody
  27. public JSONObject exceptionHandler(Exception ex) {
  28. JSONObject response = new JSONObject();
  29. response.put("message", ex.getMessage());
  30. return response;
  31. }
  32.  
  33. @ExceptionHandler(AuthException.class)
  34. @ResponseStatus(HttpStatus.UNAUTHORIZED)
  35. @ResponseBody
  36. public JSONObject authExceptionHandler(Exception ex) {
  37. JSONObject response = new JSONObject();
  38. response.put("message", ex.getMessage());
  39. return response;
  40. }
  41. }

3.7、其他model类或工具类

  User类

  1. package com.oy.model;
  2. public class User {
  3. private String username;
  4. private String password;
  5. private String role;
  6. public User() {}
  7. public User(String username, String password, String role) {
  8. this.username = username;
  9. this.password = password;
  10. this.role = role;
  11. }
  12. // getter和setter方法省略
  13. }

  AuthException类

  1. package com.oy.util;
  2. @SuppressWarnings("serial")
  3. public class AuthException extends RuntimeException {
  4. public AuthException() {}
  5. public AuthException(String message) {
  6. super(message);
  7. }
  8. }

  Response类

  1. package com.oy.util;
  2. import com.alibaba.fastjson.JSONObject;
  3.  
  4. public class Response {
  5. private int code = 0;
  6. private String msg = "";
  7. private String data = "";
  8. private JSONObject responseJson = new JSONObject(true);
  9.  
  10. public Response(int code) {
  11. this.code = code;
  12. }
  13.  
  14. public Response(int code, String msg) {
  15. this.code = code;
  16. this.msg = msg;
  17. }
  18.  
  19. public Response(String data) {
  20. this.code = 0;
  21. this.data = data;
  22. }
  23.  
  24. public String toString() {
  25. responseJson.put("code", this.code);
  26. if (!msg.isEmpty()) {
  27. responseJson.put("message", this.msg);
  28. }
  29. if (!data.isEmpty()) {
  30. responseJson.put("data", this.data);
  31. }
  32.  
  33. return responseJson.toJSONString();
  34. }
  35.  
  36. public JSONObject toJson() {
  37. responseJson.put("code", this.code);
  38. if (!msg.isEmpty()) {
  39. responseJson.put("message", this.msg);
  40. }
  41. if (!data.isEmpty()) {
  42. responseJson.put("data", this.data);
  43. }
  44.  
  45. return responseJson;
  46. }
  47. }

  UtilFunctions类

  1. package com.oy.util;
  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. public class UtilFunctions {
  5. public static Logger log = LoggerFactory.getLogger("jwt");
  6. }

3.8、测试

  1.  

springboot + 注解 + 拦截器 + JWT 实现角色权限控制的更多相关文章

  1. struts2学习笔记--拦截器(Interceptor)和登录权限验证Demo

    理解 Interceptor拦截器类似于我们学过的过滤器,是可以在action执行前后执行的代码.是我们做web开发是经常使用的技术,比如权限控制,日志.我们也可以把多个interceptor连在一起 ...

  2. springboot + redis + 注解 + 拦截器 实现接口幂等性校验

    一.概念 幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次 比如: 订单接口, 不能多次创建订单 支付接口, 重复支付同一笔订单只能扣一次钱 支付宝回调接口, 可能会多 ...

  3. Springboot + redis + 注解 + 拦截器来实现接口幂等性校验

    Springboot + redis + 注解 + 拦截器来实现接口幂等性校验   1. SpringBoot 整合篇 2. 手写一套迷你版HTTP服务器 3. 记住:永远不要在MySQL中使用UTF ...

  4. Spring MVC 方法注解拦截器

    应用场景,在方法级别对本次调用进行鉴权,如api接口中有个用户唯一标示accessToken,对于有accessToken的每次请求可以在方法加一个拦截器,获得本次请求的用户,存放到request或者 ...

  5. SpringBoot 注册拦截器方式及拦截器如何获取spring bean实例

    SpringBoot 注册拦截器时,如果用New对象的方式的话,如下: private void addTokenForMallInterceptor(InterceptorRegistry regi ...

  6. SpringMVC拦截器与SpringBoot自定义拦截器

    首先我们先回顾一下传统拦截器的写法: 第一步创建一个类实现HandlerInterceptor接口,重写接口的方法. 第二步在XML中进行如下配置,就可以实现自定义拦截器了 SpringBoot实现自 ...

  7. SpringMVC拦截器(资源和权限管理)

    1.DispatcherServlet SpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServlet.    DispatcherServle ...

  8. SpringBoot自定义拦截器实现IP白名单功能

    SpringBoot自定义拦截器实现IP白名单功能 转载请注明源地址:http://www.cnblogs.com/funnyzpc/p/8993331.html 首先,相关功能已经上线了,且先让我先 ...

  9. 11. SpringMVC拦截器(资源和权限管理)

    1.DispatcherServlet SpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServlet.     DispatcherServl ...

随机推荐

  1. 理解ES6中的Symbol

    一.为什么ES6引入Symbol 有时候我们在项目开发的过程中可能会遇到这样的问题,我写了一个对象,而另外的同时则在这个对象里面添加了一个属性或是方法,倘若添加的这个属性或是方法是原本的对象中本来就有 ...

  2. 【MM系列】SAP 簇表 A017 物料信息记录 (指定工厂) 包含的透明表

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP 簇表 A017 物料信息记录 ...

  3. 各种CNN模型

    Resnet: model_urls = { 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', 'res ...

  4. Linux setup

    Install centos 7: Config Network a config example in /etc/sysconfig/network-scripts/ifcfg-ens160 TYP ...

  5. 12.持久性后门----Ettercap之ARP中毒----RAR/ZIP & linux密码破解----kali上检测rootkits

    持久性后门 生成PHP shell weevely generate 密码 /root/Desktop/404.php 靶机IP/404.php weevely http://192.168.1.10 ...

  6. 使用ntpdate 同步 linux的时间

    1. linux 查看时间和时区的命令 timedatectl 效果为: Local time: Sun -- :: CST Universal time: Sun -- :: UTC RTC tim ...

  7. [转帖]Oracle 使用sqlnet.ora/trigger限制/允许某IP或IP段访问指定用户

    Oracle 使用sqlnet.ora/trigger限制/允许某IP或IP段访问指定用户 原创 Oracle 作者:maohaiqing0304 时间:2016-05-03 17:05:46  17 ...

  8. springboot无法找到mapper😵

    今天在学习springboot的过程中遇到mapper无法找到的问题,困扰了很久

  9. 在SSIS包中使用 Checkpoint从失败处重新启动包[转]

    使用SSIS做ETL的过程中会遇到各种各样的错误,对于一些大数据量的Job失败以后我们不希望重新运行,因为重新运行的时间开销是非常大的,我们只希望从失败的部分开始运行,这样可以省去很多的时间. SSI ...

  10. qtreewidget 显示保存xml文件

    此文是读取和存储已知结构的xml,对于未知结构的xml,可以用递归方法读取和遍历.可参考文章:Qt遍历不规则树的节点. 1.QTreewidget设置 //折叠图标(三角图标)换成自定义图标 ui-& ...