spring boot+jwt 权限验证
Posted kevinmanlee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring boot+jwt 权限验证相关的知识,希望对你有一定的参考价值。
上周看了一下jwt以前公司的开发都是使用session共享的方法。现在主流的两种方式一种是把登录信息保存在服务器,另一种则是把信息保存在客户端。在使用session 存储的时候会遇到很多的问题,随着项目越来越多工作量会变得越来越大。现在公司要开始一个新的项目,索性就开始使用jwt,数据保存在客户端每一次请求都回传给服务器验证一下。
本文分为两部分第一部分简单介绍一下JWT,第二部分简单介绍一下使用spring boot+jwt构建一个项目。
一、什么是JWT?
JWT全程JSON Web tokens 主要由 三部分组成通过“.”分割开,三部分分别是Header、Payload、Signature因此一个完成的JWT经典结构体应该是xxxx.yyyy.zzzzz
1.Header (头)
头部是一个包括了两部分的JSON 一部分是签名的算法(alg)通常使用HS256或者RSA,这里基本上都是使用HS256,还有一个部分是签名类型(typ)即JWT
例如:
"alg": "HS256", "typ": "JWT"
然后把这个JSON Base64Url,形成第一部分。
2.Payload (数据)
第二部分也是一个JSON对像,官方提供了几个数据字段 iss (issuer):签发人、exp (expiration time):过期时间、sub (subject):主题、aud (audience):受众、nbf (Not Before):生效时间、iat (Issued At):签发时间、jti (JWT ID):编号
除了官方字段还可以定义私有字段
如例:
"sub": "1234567890", "name": "John Doe", "admin": true
然后和第一部分一样使用Base64Url算法转化一下,由于这部分直接是暴露出去的顾不应该放比较重要的数据。
3.Signature
Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.
)分隔,就可以返回给用户。
关于jwt的介绍就到这里 ,参考了网上的其他人博客和JWT官网想要了解的更加详细可以前去官网仔细阅读一下。
二、构建demo
首先新建一个spring boot 项目 pom文件如下
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.7.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.58</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
然后新建一个实体类user 包括了id,name,password和一个EntRespon用于网络之间消息的传输。
public class User private String id; private String name; private String password; public String getId() return id; public void setId(String id) this.id = id; public String getName() return name; public void setName(String name) this.name = name; public String getPassword() return password; public void setPassword(String password) this.password = password;
public class EntRespson private int code; private String resultMsg; private Object data; public EntRespson() this.code=0; public int getCode() return code; public void setCode(int code) this.code = code; public String getResultMsg() return resultMsg; public void setResultMsg(String resultMsg) this.resultMsg = resultMsg; public Object getData() return data; public void setData(Object data) this.data = data;
然后新建一个controller 先写一个测试方法 test 先测试没有问题 。然后我们需要完成一个JWT加密解密的方法,以下使我们的一个简单的JWT加密解密的方法。
public class JwtUtils public static final String TOKEN_HEADER = "Authorization"; public static final String TOKEN_PREFIX = "Bearer "; private static final String SECRET = "jwtdemo"; private static final String ISS = "echisan"; // 过期时间 private static final long EXPIRATION = 360000l; /** * 加密 jwt token * @param id * @return */ public static String encode(String id) Algorithm algorithm = Algorithm.HMAC256(SECRET); String token = JWT.create() //设置过期时间为一个小时 .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION)) //设置负载 .withJWTId(id) .sign(algorithm); return token; /** * 解密 jwt toke * @param token * @return */ public static String decode(String token) if (token == null || token.length() == 0) throw new RuntimeException("token为空:" + token); Algorithm algorithm = Algorithm.HMAC256(SECRET); JWTVerifier jwtVerifier = JWT.require(algorithm).build(); DecodedJWT decodedJWT = jwtVerifier.verify(token); return decodedJWT.getId();
由于我们这demo没有连接数据库所以我把用户信息写在配置文件里面了 application.properties 如下
demo.name=lee demo.password=123 demo.id=1
然后我们完成一个读取配置文件的类
@Configuration @PropertySource("classpath:application.properties") public class UserConfig @Value("$demo.name") private String name; @Value("$demo.password") private String password; @Value("$demo.id") private String id; public String getName() return name; public void setName(String name) this.name = name; public String getPassword() return password; public void setPassword(String password) this.password = password; public String getId() return id; public void setId(String id) this.id = id;
这个时候完成controller 里面的登录方法,在登录完成后 生成一个token返还给前端这个就是一个获取受保护的凭证。
@RestController public class CtrlUser @Autowired private UserConfig config; @RequestMapping("test1") public String test() return "ok test" + config.getName(); @RequestMapping(value = "login", method = RequestMethod.POST) public EntRespson login(@RequestBody User user) EntRespson entRespson = new EntRespson(); try String usename = config.getName(); String pwds = config.getPassword(); if (!(user.getName().equals(usename) && user.getPassword().equals(pwds)))throw new Exception("账户密码错误"); //登录成功获得token String token = JwtUtils.encode(config.getId()); JSONObject jsonObject = new JSONObject(); jsonObject.put("token",token); entRespson.setData(jsonObject); catch (Exception e) entRespson.setCode(-1); entRespson.setResultMsg(e.getMessage()); return entRespson;
现在我们还是完成一个简单的拦截器的功能。
public class Interceptor implements HandlerInterceptor @Autowired private UserConfig config; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception String token = request.getHeader("token");// HttpServletRequest 请求头中取出 token if (!(handler instanceof HandlerMethod)) return true; // HandlerMethod handlerMethod = (HandlerMethod) handler; // Method method = handlerMethod.getMethod(); // //检查是否有passtoken注释,有则跳过认证 // if (method.isAnnotationPresent(SkipToken.class)) // SkipToken passToken = method.getAnnotation(SkipToken.class); // if (passToken.required()) // return true; // // String id = JwtUtils.decode(token); if (id.equals(config.getId())) return true; return false; @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
把拦截器添加到配置类中
@Configuration public class InterceptorConfig implements WebMvcConfigurer @Override public void addInterceptors(InterceptorRegistry registry) registry.addInterceptor(getInterceptor()) .addPathPatterns("/**") //配置拦截所有请求 .excludePathPatterns("/login"); // 不拦截登录请求。 @Bean public Interceptor getInterceptor() return new Interceptor();
好了 到此为止我们的demo已经完成了我们开始测试我们是demo。
启动完成之后我们先试试127.0.0.1:8080/test1
这里可以看到我们请求被拒绝了 ,然后我们测试一下登录接口。
到这里我们已经获取到了token,前端在获取了token之后前端是逻辑是需要在每一次请求的Headers里面增加一个token,我们在上一个测试接口里面加上token重新请求一下
好的在这里我们已经能看到了我们带有token的请求已经访问成功了。
最后附上源码地址:https://github.com/llcin/spring_tool/tree/master/mydemo
以上是关于spring boot+jwt 权限验证的主要内容,如果未能解决你的问题,请参考以下文章
使用 Okta Spring Boot Starter 的自定义权限
Spring Boot + Apache Shrio + JWT前后端分离项目