springboot + 注解 + 拦截器 + JWT 实现角色权限控制
1、关于JWT,参考:
(2)认识JWT
2、JWT的JAVA实现
Java中对JWT的支持可以考虑使用JJWT开源库;JJWT实现了JWT, JWS, JWE 和 JWA RFC规范;下面将简单举例说明其使用:
2.1、生成Token码
- import javax.crypto.spec.SecretKeySpec;
- import javax.xml.bind.DatatypeConverter;
- import java.security.Key;
- import io.jsonwebtoken.*;
- import java.util.Date;
- //Sample method to construct a JWT
- private String createJWT(String id, String issuer, String subject, long ttlMillis) {
- //The JWT signature algorithm we will be using to sign the token
- SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
- long nowMillis = System.currentTimeMillis();
- Date now = new Date(nowMillis);
- //We will sign our JWT with our ApiKey secret
- byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(apiKey.getSecret());
- Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
- //Let's set the JWT Claims
- JwtBuilder builder = Jwts.builder().setId(id)
- .setIssuedAt(now)
- .setSubject(subject)
- .setIssuer(issuer)
- .signWith(signatureAlgorithm, signingKey);
- //if it has been specified, let's add the expiration
- if (ttlMillis >= 0) {
- long expMillis = nowMillis + ttlMillis;
- Date exp = new Date(expMillis);
- builder.setExpiration(exp);
- }
- //Builds the JWT and serializes it to a compact, URL-safe string
- return builder.compact();
- }
2.2、解码和验证Token码
- import javax.xml.bind.DatatypeConverter;
- import io.jsonwebtoken.Jwts;
- import io.jsonwebtoken.Claims;
- //Sample method to validate and read the JWT
- private void parseJWT(String jwt) {
- //This line will throw an exception if it is not a signed JWS (as expected)
- Claims claims = Jwts.parser()
- .setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret()))
- .parseClaimsJws(jwt).getBody();
- System.out.println("ID: " + claims.getId());
- System.out.println("Subject: " + claims.getSubject());
- System.out.println("Issuer: " + claims.getIssuer());
- System.out.println("Expiration: " + claims.getExpiration());
- }
3、springboot + 注解 + 拦截器 + JWT 实现角色权限控制
demo涉及的技术:springboot 2.1.5.RELEASE + 注解 + 拦截器 + JWT。
demo功能:模拟角色权限控制。使用注解@Role标注的Controller方法,都需要进行token验证,判断token中role的值与@Role注解中role的值是否相同。
3.1、pom文件
- <dependencies>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt</artifactId>
- <version>0.7.0</version>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>1.2.47</version>
- </dependency>
- </dependencies>
3.2、token工具类JwtUtil
- package com.oy.util;
- import java.util.Date;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Map.Entry;
- import javax.servlet.http.HttpServletRequest;
- import org.junit.jupiter.api.Test;
- import com.oy.model.User;
- import io.jsonwebtoken.Jwts;
- import io.jsonwebtoken.SignatureAlgorithm;
- public class JwtUtil {
- public static final long EXPIRATION_TIME = 3600_000; // 1 hour
- // public static final long EXPIRATION_TIME = 0; // for test
- public static final String SECRET = "secret007";
- public static final String TOKEN_PREFIX = "Bearer";
- public static final String HEADER_STRING = "Authorization";
- // 生成token
- public static String generateToken(User user) {
- //you can put any data into the map
- HashMap<String, Object> map = new HashMap<>();
- map.put("name", user.getUsername());
- map.put("role", user.getRole());
- String jwt = Jwts.builder()
- .setClaims(map) // 数据
- .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // 过期时间
- .signWith(SignatureAlgorithm.HS512, SECRET) // 算法,密钥
- .compact();
- return TOKEN_PREFIX + " " + jwt;
- }
- // 验证和解析token
- public static Map<String, Object> validateTokenAndGetClaims(HttpServletRequest request) {
- String token = request.getHeader(HEADER_STRING); // Authorization
- if (token == null)
- throw new TokenValidationException("Missing token");
- // Parse the token. Throw exception when token is invalid
- Map<String, Object> body = Jwts.parser()
- .setSigningKey(SECRET)
- .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
- .getBody();
- return body;
- }
- @SuppressWarnings("serial")
- public static class TokenValidationException extends RuntimeException {
- public TokenValidationException(String msg) {
- super(msg);
- }
- }
- // 测试生成jwt
- @Test
- public void test1() {
- UtilFunctions.log.info(generateToken(new User("admin", "", "admin")));
- // Bearer eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiYWRtaW4iLCJuYW1lIjoiYWRtaW4iLCJleHAiOjE1NjAwNzI1MTN9.YRG59eqP8nIoRNURPRYZWv3SAtss9YLOXjRsVmLmms7qMImq4MsERN0QuDbLGorgLCAbrIiSJjBY5_DaPJqP6Q
- }
- // 测试验证和解析token
- // 测试使用错误的密钥: io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature.
- // JWT validity cannot be asserted and should not be trusted.
- @Test
- public void test2() {
- String token = "eyJhbGciOiJIUzUxMiJ9.eyJyb2xlIjoiYWRtaW4iLCJuYW1lIjoiYWRtaW4iLCJleHAiOjE1NjAwNzI1MTN9.YRG59eqP8nIoRNURPRYZWv3SAtss9YLOXjRsVmLmms7qMImq4MsERN0QuDbLGorgLCAbrIiSJjBY5_DaPJqP6Q";
- Map<String, Object> body = Jwts.parser()
- //.setSigningKey(SECRET)
- .setSigningKey("secret") // 测试使用错误的密钥
- .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
- .getBody();
- for (Entry<String, Object> entry : body.entrySet()) {
- UtilFunctions.log.info("=== key:{}, value:{} ===", entry.getKey(), entry.getValue());
- // === key:role, value:admin ===
- // === key:name, value:admin ===
- // === key:exp, value:1560072513 ===
- }
- }
- }
3.3、注解@Role的定义
- package com.oy.filter;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface Role {
- String role();
- }
3.4、拦截器RoleInterceptor
- package com.oy.filter;
- import java.util.Map;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.web.method.HandlerMethod;
- import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
- import com.oy.util.AuthException;
- import com.oy.util.JwtUtil;
- import com.oy.util.UtilFunctions;
- public class RoleInterceptor extends HandlerInterceptorAdapter {
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- UtilFunctions.log.info("RoleInterceptor work...");
- // target of request is method of controller
- if (handler instanceof HandlerMethod) {
- HandlerMethod handlerMethod = (HandlerMethod) handler;
- Role roleAnnotation = handlerMethod.getMethodAnnotation(Role.class);
- if (roleAnnotation == null) { // method without @Role annotation
- UtilFunctions.log.info("RoleInterceptor over...");
- return true; // 放行
- } else { // method with @Role annotation
- // 验证token
- String role = null;
- try {
- Map<String, Object> claims = JwtUtil.validateTokenAndGetClaims(request);
- UtilFunctions.log.info("claims:{}", claims);
- role = String.valueOf(claims.get("role")); // 从token中取数据: role
- // roleAnnotation.role(): 获取注解中指定role
- UtilFunctions.log.info("=== role:{}, roleAnnotation.role:{} ===", role, roleAnnotation.role());
- if (System.currentTimeMillis() / 1000L > (int) claims.get("exp")) {
- throw new AuthException("token 过期了...");
- }
- } catch (Exception e) {
- // response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
- // UtilFunctions.log.info(e.toString() + ". RoleInterceptor over...");
- // return false; // 拦截
- // token验证不通过,拦截
- throw new AuthException(e.getMessage());
- }
- if (role == null || !role.equals(roleAnnotation.role())) {
- // response.setStatus(401);
- // UtilFunctions.log.info("RoleInterceptor over...");
- // return false; // 拦截
- throw new AuthException("a role of " + roleAnnotation.role() + " is needed, but you are " + role);
- }
- }
- }
- UtilFunctions.log.info("RoleInterceptor over...");
- return true; // // 放行
- }
- }
在sprigboot中注册拦截器
- package com.oy;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.EnableWebMvc;
- import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
- import com.oy.filter.RoleInterceptor;
- @EnableWebMvc
- @Configuration
- public class webConfig implements WebMvcConfigurer {
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- // 拦截除登陆外的所有请求
- registry.addInterceptor(new RoleInterceptor())
- .addPathPatterns("/**").excludePathPatterns("/login");
- }
- }
3.5、IndexController
- package com.oy.controller;
- import java.util.HashMap;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.http.HttpStatus;
- import org.springframework.http.ResponseEntity;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.PostMapping;
- import org.springframework.web.bind.annotation.RequestParam;
- import org.springframework.web.bind.annotation.RestController;
- import com.alibaba.fastjson.JSONObject;
- import com.oy.filter.Role;
- import com.oy.model.User;
- import com.oy.util.JwtUtil;
- import com.oy.util.Response;
- import com.oy.util.UtilFunctions;
- @RestController
- public class IndexController {
- @Role(role = "user")
- @GetMapping("/test1")
- public JSONObject test1() {
- return new Response("test role=user").toJson();
- }
- @Role(role = "admin")
- @GetMapping("/test2")
- public JSONObject test2() {
- return new Response("test role=admin").toJson();
- }
- // 登录方法不拦截
- @SuppressWarnings({ "serial", "rawtypes" })
- @PostMapping("/login")
- public Object login(HttpServletResponse response,
- @RequestParam(value = "username", required = true) String username,
- @RequestParam(value = "password", required = true) String password) throws Exception {
- UtilFunctions.log.info("login info, username:{}, password:{}", username, password);
- User user = isValidUsernameAndPassword(username, password);
- if (user != null) {
- String jwt = JwtUtil.generateToken(user);
- return new HashMap<String, String>() {
- {
- put("token", jwt);
- }
- };
- } else {
- //
- return new ResponseEntity(HttpStatus.UNAUTHORIZED);
- }
- }
- private User isValidUsernameAndPassword(String username, String password) {
- if ("admin".equals(username) && "admin123".equals(password)
- || "user".equals(username) && "user123".equals(password)) {
- return new User("admin", "", "admin");
- }
- return null;
- }
- }
3.6、全局异常处理ExceptionHandlerController
- package com.oy.controller;
- import org.springframework.http.HttpStatus;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.bind.annotation.ResponseStatus;
- import com.alibaba.fastjson.JSONObject;
- import com.oy.util.AuthException;
- @ControllerAdvice
- public class ExceptionHandlerController {
- @ExceptionHandler(RuntimeException.class)
- @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
- @ResponseBody
- public JSONObject runtimeExceptionHandler(RuntimeException ex) {
- JSONObject response = new JSONObject();
- response.put("message", ex.getMessage());
- return response;
- }
- @ExceptionHandler(Exception.class)
- @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
- @ResponseBody
- public JSONObject exceptionHandler(Exception ex) {
- JSONObject response = new JSONObject();
- response.put("message", ex.getMessage());
- return response;
- }
- @ExceptionHandler(AuthException.class)
- @ResponseStatus(HttpStatus.UNAUTHORIZED)
- @ResponseBody
- public JSONObject authExceptionHandler(Exception ex) {
- JSONObject response = new JSONObject();
- response.put("message", ex.getMessage());
- return response;
- }
- }
3.7、其他model类或工具类
User类
- package com.oy.model;
- public class User {
- private String username;
- private String password;
- private String role;
- public User() {}
- public User(String username, String password, String role) {
- this.username = username;
- this.password = password;
- this.role = role;
- }
- // getter和setter方法省略
- }
AuthException类
- package com.oy.util;
- @SuppressWarnings("serial")
- public class AuthException extends RuntimeException {
- public AuthException() {}
- public AuthException(String message) {
- super(message);
- }
- }
Response类
- package com.oy.util;
- import com.alibaba.fastjson.JSONObject;
- public class Response {
- private int code = 0;
- private String msg = "";
- private String data = "";
- private JSONObject responseJson = new JSONObject(true);
- public Response(int code) {
- this.code = code;
- }
- public Response(int code, String msg) {
- this.code = code;
- this.msg = msg;
- }
- public Response(String data) {
- this.code = 0;
- this.data = data;
- }
- public String toString() {
- responseJson.put("code", this.code);
- if (!msg.isEmpty()) {
- responseJson.put("message", this.msg);
- }
- if (!data.isEmpty()) {
- responseJson.put("data", this.data);
- }
- return responseJson.toJSONString();
- }
- public JSONObject toJson() {
- responseJson.put("code", this.code);
- if (!msg.isEmpty()) {
- responseJson.put("message", this.msg);
- }
- if (!data.isEmpty()) {
- responseJson.put("data", this.data);
- }
- return responseJson;
- }
- }
UtilFunctions类
- package com.oy.util;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- public class UtilFunctions {
- public static Logger log = LoggerFactory.getLogger("jwt");
- }
3.8、测试
springboot + 注解 + 拦截器 + JWT 实现角色权限控制的更多相关文章
- struts2学习笔记--拦截器(Interceptor)和登录权限验证Demo
理解 Interceptor拦截器类似于我们学过的过滤器,是可以在action执行前后执行的代码.是我们做web开发是经常使用的技术,比如权限控制,日志.我们也可以把多个interceptor连在一起 ...
- springboot + redis + 注解 + 拦截器 实现接口幂等性校验
一.概念 幂等性, 通俗的说就是一个接口, 多次发起同一个请求, 必须保证操作只能执行一次 比如: 订单接口, 不能多次创建订单 支付接口, 重复支付同一笔订单只能扣一次钱 支付宝回调接口, 可能会多 ...
- Springboot + redis + 注解 + 拦截器来实现接口幂等性校验
Springboot + redis + 注解 + 拦截器来实现接口幂等性校验 1. SpringBoot 整合篇 2. 手写一套迷你版HTTP服务器 3. 记住:永远不要在MySQL中使用UTF ...
- Spring MVC 方法注解拦截器
应用场景,在方法级别对本次调用进行鉴权,如api接口中有个用户唯一标示accessToken,对于有accessToken的每次请求可以在方法加一个拦截器,获得本次请求的用户,存放到request或者 ...
- SpringBoot 注册拦截器方式及拦截器如何获取spring bean实例
SpringBoot 注册拦截器时,如果用New对象的方式的话,如下: private void addTokenForMallInterceptor(InterceptorRegistry regi ...
- SpringMVC拦截器与SpringBoot自定义拦截器
首先我们先回顾一下传统拦截器的写法: 第一步创建一个类实现HandlerInterceptor接口,重写接口的方法. 第二步在XML中进行如下配置,就可以实现自定义拦截器了 SpringBoot实现自 ...
- SpringMVC拦截器(资源和权限管理)
1.DispatcherServlet SpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServlet. DispatcherServle ...
- SpringBoot自定义拦截器实现IP白名单功能
SpringBoot自定义拦截器实现IP白名单功能 转载请注明源地址:http://www.cnblogs.com/funnyzpc/p/8993331.html 首先,相关功能已经上线了,且先让我先 ...
- 11. SpringMVC拦截器(资源和权限管理)
1.DispatcherServlet SpringMVC具有统一的入口DispatcherServlet,所有的请求都通过DispatcherServlet. DispatcherServl ...
随机推荐
- 理解ES6中的Symbol
一.为什么ES6引入Symbol 有时候我们在项目开发的过程中可能会遇到这样的问题,我写了一个对象,而另外的同时则在这个对象里面添加了一个属性或是方法,倘若添加的这个属性或是方法是原本的对象中本来就有 ...
- 【MM系列】SAP 簇表 A017 物料信息记录 (指定工厂) 包含的透明表
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[MM系列]SAP 簇表 A017 物料信息记录 ...
- 各种CNN模型
Resnet: model_urls = { 'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth', 'res ...
- Linux setup
Install centos 7: Config Network a config example in /etc/sysconfig/network-scripts/ifcfg-ens160 TYP ...
- 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 ...
- 使用ntpdate 同步 linux的时间
1. linux 查看时间和时区的命令 timedatectl 效果为: Local time: Sun -- :: CST Universal time: Sun -- :: UTC RTC tim ...
- [转帖]Oracle 使用sqlnet.ora/trigger限制/允许某IP或IP段访问指定用户
Oracle 使用sqlnet.ora/trigger限制/允许某IP或IP段访问指定用户 原创 Oracle 作者:maohaiqing0304 时间:2016-05-03 17:05:46 17 ...
- springboot无法找到mapper😵
今天在学习springboot的过程中遇到mapper无法找到的问题,困扰了很久
- 在SSIS包中使用 Checkpoint从失败处重新启动包[转]
使用SSIS做ETL的过程中会遇到各种各样的错误,对于一些大数据量的Job失败以后我们不希望重新运行,因为重新运行的时间开销是非常大的,我们只希望从失败的部分开始运行,这样可以省去很多的时间. SSI ...
- qtreewidget 显示保存xml文件
此文是读取和存储已知结构的xml,对于未知结构的xml,可以用递归方法读取和遍历.可参考文章:Qt遍历不规则树的节点. 1.QTreewidget设置 //折叠图标(三角图标)换成自定义图标 ui-& ...