SpringBoot集成JWT实现Token |
您所在的位置:网站首页 › springboot集成jwt获取token › SpringBoot集成JWT实现Token |
SpringBoot 2.1.4集成JWT实现token验证
wRitchie 2019-05-16 15:11:24 SpringBoot 2.1.4集成JWT实现token验证 编者: wRitchie(吴理琪) 来源:http://www.bj9420.com 什么是JWT:Json web token (JWT) 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519)。定义了一种简洁的,自包含的方法用于通信双方之间以JSON对象的形式安全的传递信息。因为数字签名的存在,这些信息是可信的,JWT可以使用HMAC算法或者是RSA或ECDSA的公私秘钥对进行签名。 JWT如何获取访问令牌(token)并用于访问资源(API)流程:1、应用端向权限服务器请求授权;2、权限服务器授权成功向应用端返回一个访问令牌(token);3、应用端使用访问令牌(token)访问受保护的资源(如API)。
JWT是由三段信息构成,将这三段信息文本用“.“连接一起就构成了JWT字符串,Header.Payload.Signature,例如:eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE1NTgwNjI2OTYsInVzZXJJZCI6IjEifQ.XiI0xjX0izVeJRmhbXN1w1fXKdHB0wsc9teFKq84pclpJt6yS2k0BVXAklHrke_nz6XtcCyi1hgvpn8bf95gwg。 第一步:pom.xml添加依赖,引入jar包: 版本声明 0.9.1版本依赖: io.jsonwebtoken jjwt ${jjwt.version} 第二步:实现JWT的token验证共3个Java类。首先注册一个检验JWT的过滤器jwtFilter, 通过jwtFilter过滤器实现对每个Rest API请求都验证JWT的功能。 其中JwtAuthenticationFilter继承了OncePerRequestFilter,任何请求都会先经过jwtFilter过滤器, 然后选择让合法JWT请求通过jwtFilter。 具体在SpringBoot的Application启动类中增加@Bean注解,代码如下: @Bean public FilterRegistrationBean jwtFilter() { logger.info("JWT Filter 运行中..."); final FilterRegistrationBean registrationBean = new FilterRegistrationBean(); JwtAuthenticationFilter filter = new JwtAuthenticationFilter(); registrationBean.setFilter(filter); return registrationBean; } 第三步:再看JWT权限过滤器JwtAuthenticationFilter.java,JwtAuthenticationFilter类继承了OncePerRequestFilter抽象类, 确保任何用户请求资源都会运行doFilterInternal方法。此处将从HTTP Header里面截取JWT, 并且验证JWT的签名和过期时间,若有问题,会返回HTTP 401错误。 package com.bj9420.jwt; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.AntPathMatcher; import org.springframework.util.PathMatcher; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @Author: wRitchie * @Description: JWT权限过滤器 * @Param: * @return: * @Date: 2019/1/14 14:32 */ public class JwtAuthenticationFilter extends OncePerRequestFilter { private static final Logger log = LoggerFactory.getLogger(JwtAuthenticationFilter.class); private static final PathMatcher pathMatcher = new AntPathMatcher(); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { log.info("JWT权限过滤器 JwtAuthenticationFilter开始..."); try { String servletPath = request.getServletPath(); if(isProtectedUrl(request)) { log.info("私密API请求:"+servletPath); request = JwtUtil.validateTokenAndAddUserIdToHeader(request); }else{ log.info("开放API请求:"+servletPath); } } catch (Exception e) { log.info("JWT权限过滤器异常:"+e); response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); return; } filterChain.doFilter(request, response); } /** * @Author: wRitchie * @Description: 是否需要token验证 路径中url含api,则返回true,不含则返回false * @Param: [request] * @return: boolean * @Date: 2019/1/14 14:34 */ private boolean isProtectedUrl(HttpServletRequest request) { /** 对路径中url含有api的请求的返回true */ boolean matchFlag = pathMatcher.match("/**/api/**", request.getServletPath()); return matchFlag; } } 第四步: JwtUtil.java工具类,主要是生成令牌方法和验证令牌是否有效,具体如下: package com.bj9420.jwt; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import java.util.*; /** * @Author: wRitchie * @Description: JwtUtil工具类 * @Param: * @return: * @Date: 2019/1/14 15:14 */ public class JwtUtil { private static final Logger log = LoggerFactory.getLogger(JwtUtil.class); /** * 失效时间: 1 天 =24*3600*1000 ms */ public static final long EXPIRATION_TIME = 1 * 24 * 3600 * 1000; /** * 私钥 */ public static final String SECRET = "http://www.bj9420.com/jwt"; /** * token 前缀 */ public static final String TOKEN_PREFIX = "Bearer "; /** * Authorization header */ public static final String HEADER_STRING = "Authorization"; /** * 保存的数据字段名称 */ public static final String USER_NAME = "userId"; public static String generateToken(String userId) { HashMap map = new HashMap(); //可以将自定义相关的数据放入Map中 map.put(USER_NAME, userId); String jwt = Jwts.builder() .setClaims(map) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); //jwt前面一般都会加Bearer return TOKEN_PREFIX + jwt; } public static HttpServletRequest validateTokenAndAddUserIdToHeader(HttpServletRequest request) { String token = request.getHeader(HEADER_STRING); log.info("请求token:" + token); if (token != null) { // 解析令牌token. try { Map body = Jwts.parser() .setSigningKey(SECRET) .parseClaimsJws(token.replace(TOKEN_PREFIX, "")) .getBody(); //遍历自定的相关数据,可以删除 for (Map.Entry entry : body.entrySet()) { log.info("****JWT paser Key = " + entry.getKey() + ", Value = " + entry.getValue()); } return new CustomHttpServletRequest(request, body); } catch (Exception e) { log.info(e.getMessage()); throw new TokenValidationException(e.getMessage()); } } else { throw new TokenValidationException("The token is invalid!"); } } public static class CustomHttpServletRequest extends HttpServletRequestWrapper { private Map claims; public CustomHttpServletRequest(HttpServletRequest request, Map claims) { super(request); this.claims = new HashMap(); claims.forEach((k, v) -> this.claims.put(k, String.valueOf(v))); } @Override public Enumeration getHeaders(String name) { if (claims != null && claims.containsKey(name)) { return Collections.enumeration(Arrays.asList(claims.get(name))); } return super.getHeaders(name); } public Map getClaims() { return claims; } } static class TokenValidationException extends RuntimeException { public TokenValidationException(String msg) { super(msg); } } } 第五步:在控制类中编写接口,如LoginController.java,对于需要令牌token验证的,可以相应的请求路径中加JwtAuthenticationFilter 类中定义的匹配规则标识符,如“api“,例如在LoginController类中,登录方法public Result login(@RequestBody User user) ,注解路径映射@PostMapping("/login")中,不含“api”,故该方法无需令牌token验证;根据用户ID获取用户信息方法public Result getUserInfo(@RequestHeader(value = JwtUtil.USER_NAME) String userId),注解路径映射@GetMapping("/api/getUserInfo")中含有“api”,故该方法令牌token验证;如要总个控制类中的方法都需令牌token验证,则在类的注解路径映射中加“api”,例如:注解路径映射@RequestMapping("loginController")改为注解路径映射@RequestMapping("/api/loginController"),具体示例代码如下: package com.bj9420.controller.login; import com.bj9420.controller.common.BaseController; import com.bj9420.framework.SystemConstant; import com.bj9420.framework.util.AESUtil; import com.bj9420.framework.util.MD5Util; import com.bj9420.framework.util.StringUtil; import com.bj9420.jwt.JwtUtil; import com.bj9420.model.Menu; import com.bj9420.model.Result; import com.bj9420.model.User; import com.bj9420.service.menu.IMenuService; import com.bj9420.service.user.IUserService; import io.jsonwebtoken.Jwts; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Title: LoginController.java * @Description: 登录控制类 * @author: wRitchie * @date: 2018/12/28 15:39 * @version: V1.0 * @Copyright (c): 2018 http://bj9420.com All rights reserved. */ @RestController @RequestMapping("loginController") @Api(value = "登录控制类", tags = "登录控制类") public class LoginController extends BaseController { @Autowired private IUserService userService; @Autowired private IMenuService menuService; @GetMapping("sign") @ApiOperation(value = "登录测试", notes = "无权限验证的登录测试") public Result sign(String loginName) { logger.info("#######LoginController#######"); Map userMap = new HashMap(); userMap.put("loginName", loginName); List userList = userService.selectByPager(userMap); if (userList.size() > 0) { userMap.put("password", ""); User user = userService.selectByPrimaryKey(1); userMap.put("user", user); return Result.success(userMap); } else { return Result.failure("用户不存在,请先注册。", null); } } @PostMapping("/login") @ApiOperation(value = "登录接口", notes = "公开权限,用户登录后返回token") public Result login(@RequestBody User user) { logger.info("####### login #######" + user.getLoginName() + " password:" + user.getPassword()); String jwt=""; String pwdTmp = MD5Util.encode(user.getPassword()); //logger.info("####登录名:"+loginName+" 密码:" + password); String sKey = MD5Util.md5(SystemConstant.SYSTEM_SKEY).substring(0, 16); String pwdEncrypt = AESUtil.encrypt(pwdTmp, sKey).substring(0, 16); logger.info("密码加密:" + pwdEncrypt); Map userMap = new HashMap(); userMap.put("loginName", user.getLoginName()); List userList = userService.selectByPager(userMap); if (userList.size() > 0) { userMap = userList.get(0); String pwdDb = userMap.get("password") + ""; if (pwdDb.equals(pwdEncrypt)) { userMap.put("password", ""); jwt = JwtUtil.generateToken(userMap.get("userId") + ""); userMap.put("token", jwt); Map param=new HashMap(); param.put("userId", userMap.get("userId")); List menuList=menuService.selectBySelectiveByUserId(param); userMap.put("menuList",menuList); return Result.success(userMap); } else { logger.info("密码错误。"); userMap.put("authorized", new ResponseEntity(HttpStatus.UNAUTHORIZED)); userMap.put("token", jwt); return Result.failure("密码错误,登录失败。", userMap); } } else { logger.info("用户不存在,请先注册。"); userMap.put("authorized", new ResponseEntity(HttpStatus.UNAUTHORIZED)); userMap.put("token", jwt); return Result.failure("用户不存在,请先注册,登录失败。", userMap); } } @GetMapping("/api/getUserInfo") @ApiOperation(value = "获取用户信息", notes = "根token获取用户的信息") public Result getUserInfo(@RequestHeader(value = JwtUtil.USER_NAME) String userId) { logger.info("#######LoginController getUserInfo#######"); Map userMap = new HashMap(); User user = userService.selectByPrimaryKey(Integer.valueOf(userId)); userMap.put("user", user); return Result.success("授权成功。", userMap); } @GetMapping("/getUserByToken") @ApiOperation(value = "查询用户信息", notes = "根据token获取用户的信息") public Result getUserByToken(String token) { logger.info("#######LoginController getUserByToken#######"); Map userMap = new HashMap(); String userId = ""; try { if (token != null) { Map body = Jwts.parser() .setSigningKey(JwtUtil.SECRET) .parseClaimsJws(token.replace(JwtUtil.TOKEN_PREFIX, "")) .getBody(); for (Map.Entry entry : body.entrySet()) { logger.info("****getUserByToken JWT paser Key = " + entry.getKey() + ", Value = " + entry.getValue()); if ("userId".equals(entry.getKey())) { userId = (String) entry.getValue(); break; } } } else { logger.info("The token is null!"); return Result.failure("The token is null!",userMap); } if (!StringUtil.isEmpty(userId)) { User user = userService.selectByPrimaryKey(Integer.valueOf(userId)); userMap.put("user", user); return Result.success("根据token获取用户的信息成功。", userMap); } return Result.failure(); } catch (Exception e) { return Result.exception("根据token获取用户的信息异常:",userMap); } } } 第六步:测试API,具体结果如下图所示: 1、登录,请求服务授权,返回令牌token,如下图所示
2、根据返回的令牌token,请求根据用户ID获取用户信息API,正确的token请求API返回如下图:
3、修改返回的令牌token,使用错误的令牌token请求根据用户ID获取用户信息API,错误的token请求API返回如下图:
正确错误令牌token请求时,IDEA控制台日志信息如下图所示:
至止,SpringBoot集成JWT实验token验证完毕,根据具体的实际应用项目,适当修改即可以使用。
https://www.jianshu.com/p/54603b9933ca |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |