【From】 http://blog.csdn.net/sun_t89/article/details/51923017

Spring Boot实战之Filter实现使用JWT进行接口认证

jwt(json web token)

用户发送按照约定,向服务端发送 Header、Payload 和 Signature,并包含认证信息(密码),验证通过后服务端返回一个token,之后用户使用该token作为登录凭证,适合于移动端和api

jwt使用流程

本文示例接上面几篇文章中的代码进行编写,请阅读本文的同时可以参考前面几篇文章

1、添加依赖库jjwt,本文中构造jwt及解析jwt都使用了jjwt库

  1. <dependency>
  2. <groupId>io.jsonwebtoken</groupId>
  3. <artifactId>jjwt</artifactId>
  4. <version>0.6.0</version>
  5. </dependency>

2、添加登录获取token时,所需要的认证信息类LoginPara.java

  1. package com.xiaofangtech.sunt.jwt;
  2. public class LoginPara {
  3. private String clientId;
  4. private String userName;
  5. private String password;
  6. private String captchaCode;
  7. private String captchaValue;
  8. public String getClientId() {
  9. return clientId;
  10. }
  11. public void setClientId(String clientId) {
  12. this.clientId = clientId;
  13. }
  14. public String getUserName() {
  15. return userName;
  16. }
  17. public void setUserName(String userName) {
  18. this.userName = userName;
  19. }
  20. public String getPassword() {
  21. return password;
  22. }
  23. public void setPassword(String password) {
  24. this.password = password;
  25. }
  26. public String getCaptchaCode() {
  27. return captchaCode;
  28. }
  29. public void setCaptchaCode(String captchaCode) {
  30. this.captchaCode = captchaCode;
  31. }
  32. public String getCaptchaValue() {
  33. return captchaValue;
  34. }
  35. public void setCaptchaValue(String captchaValue) {
  36. this.captchaValue = captchaValue;
  37. }
  38. }

3、添加构造jwt及解析jwt的帮助类JwtHelper.java

  1. package com.xiaofangtech.sunt.jwt;
  2. import java.security.Key;
  3. import java.util.Date;
  4. import javax.crypto.spec.SecretKeySpec;
  5. import javax.xml.bind.DatatypeConverter;
  6. import io.jsonwebtoken.Claims;
  7. import io.jsonwebtoken.JwtBuilder;
  8. import io.jsonwebtoken.Jwts;
  9. import io.jsonwebtoken.SignatureAlgorithm;
  10. public class JwtHelper {
  11. public static Claims parseJWT(String jsonWebToken, String base64Security){
  12. try
  13. {
  14. Claims claims = Jwts.parser()
  15. .setSigningKey(DatatypeConverter.parseBase64Binary(base64Security))
  16. .parseClaimsJws(jsonWebToken).getBody();
  17. return claims;
  18. }
  19. catch(Exception ex)
  20. {
  21. return null;
  22. }
  23. }
  24. public static String createJWT(String name, String userId, String role,
  25. String audience, String issuer, long TTLMillis, String base64Security)
  26. {
  27. SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
  28. long nowMillis = System.currentTimeMillis();
  29. Date now = new Date(nowMillis);
  30. //生成签名密钥
  31. byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Security);
  32. Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
  33. //添加构成JWT的参数
  34. JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT")
  35. .claim("role", role)
  36. .claim("unique_name", name)
  37. .claim("userid", userId)
  38. .setIssuer(issuer)
  39. .setAudience(audience)
  40. .signWith(signatureAlgorithm, signingKey);
  41. //添加Token过期时间
  42. if (TTLMillis >= 0) {
  43. long expMillis = nowMillis + TTLMillis;
  44. Date exp = new Date(expMillis);
  45. builder.setExpiration(exp).setNotBefore(now);
  46. }
  47. //生成JWT
  48. return builder.compact();
  49. }
  50. }

4、添加token返回结果类AccessToken.java

  1. package com.xiaofangtech.sunt.jwt;
  2. public class AccessToken {
  3. private String access_token;
  4. private String token_type;
  5. private long expires_in;
  6. public String getAccess_token() {
  7. return access_token;
  8. }
  9. public void setAccess_token(String access_token) {
  10. this.access_token = access_token;
  11. }
  12. public String getToken_type() {
  13. return token_type;
  14. }
  15. public void setToken_type(String token_type) {
  16. this.token_type = token_type;
  17. }
  18. public long getExpires_in() {
  19. return expires_in;
  20. }
  21. public void setExpires_in(long expires_in) {
  22. this.expires_in = expires_in;
  23. }
  24. }

5、添加获取token的接口,通过传入用户认证信息(用户名、密码)进行认证获取

  1. package com.xiaofangtech.sunt.jwt;
  2. import org.springframework.beans.factory.annotation.Autowired;
  3. import org.springframework.web.bind.annotation.RequestBody;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RestController;
  6. import com.xiaofangtech.sunt.bean.UserInfo;
  7. import com.xiaofangtech.sunt.repository.UserInfoRepository;
  8. import com.xiaofangtech.sunt.utils.MyUtils;
  9. import com.xiaofangtech.sunt.utils.ResultMsg;
  10. import com.xiaofangtech.sunt.utils.ResultStatusCode;
  11. @RestController
  12. public class JsonWebToken {
  13. @Autowired
  14. private UserInfoRepository userRepositoy;
  15. @Autowired
  16. private Audience audienceEntity;
  17. @RequestMapping("oauth/token")
  18. public Object getAccessToken(@RequestBody LoginPara loginPara)
  19. {
  20. ResultMsg resultMsg;
  21. try
  22. {
  23. if(loginPara.getClientId() == null
  24. || (loginPara.getClientId().compareTo(audienceEntity.getClientId()) != 0))
  25. {
  26. resultMsg = new ResultMsg(ResultStatusCode.INVALID_CLIENTID.getErrcode(),
  27. ResultStatusCode.INVALID_CLIENTID.getErrmsg(), null);
  28. return resultMsg;
  29. }
  30. //验证码校验在后面章节添加
  31. //验证用户名密码
  32. UserInfo user = userRepositoy.findUserInfoByName(loginPara.getUserName());
  33. if (user == null)
  34. {
  35. resultMsg = new ResultMsg(ResultStatusCode.INVALID_PASSWORD.getErrcode(),
  36. ResultStatusCode.INVALID_PASSWORD.getErrmsg(), null);
  37. return resultMsg;
  38. }
  39. else
  40. {
  41. String md5Password = MyUtils.getMD5(loginPara.getPassword()+user.getSalt());
  42. if (md5Password.compareTo(user.getPassword()) != 0)
  43. {
  44. resultMsg = new ResultMsg(ResultStatusCode.INVALID_PASSWORD.getErrcode(),
  45. ResultStatusCode.INVALID_PASSWORD.getErrmsg(), null);
  46. return resultMsg;
  47. }
  48. }
  49. //拼装accessToken
  50. String accessToken = JwtHelper.createJWT(loginPara.getUserName(), String.valueOf(user.getName()),
  51. user.getRole(), audienceEntity.getClientId(), audienceEntity.getName(),
  52. audienceEntity.getExpiresSecond() * 1000, audienceEntity.getBase64Secret());
  53. //返回accessToken
  54. AccessToken accessTokenEntity = new AccessToken();
  55. accessTokenEntity.setAccess_token(accessToken);
  56. accessTokenEntity.setExpires_in(audienceEntity.getExpiresSecond());
  57. accessTokenEntity.setToken_type("bearer");
  58. resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(),
  59. ResultStatusCode.OK.getErrmsg(), accessTokenEntity);
  60. return resultMsg;
  61. }
  62. catch(Exception ex)
  63. {
  64. resultMsg = new ResultMsg(ResultStatusCode.SYSTEM_ERR.getErrcode(),
  65. ResultStatusCode.SYSTEM_ERR.getErrmsg(), null);
  66. return resultMsg;
  67. }
  68. }
  69. }

6、添加使用jwt认证的filter

  1. package com.xiaofangtech.sunt.filter;
  2. import java.io.IOException;
  3. import javax.servlet.Filter;
  4. import javax.servlet.FilterChain;
  5. import javax.servlet.FilterConfig;
  6. import javax.servlet.ServletException;
  7. import javax.servlet.ServletRequest;
  8. import javax.servlet.ServletResponse;
  9. import javax.servlet.http.HttpServletRequest;
  10. import javax.servlet.http.HttpServletResponse;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.web.context.support.SpringBeanAutowiringSupport;
  13. import com.fasterxml.jackson.databind.ObjectMapper;
  14. import com.xiaofangtech.sunt.jwt.Audience;
  15. import com.xiaofangtech.sunt.jwt.JwtHelper;
  16. import com.xiaofangtech.sunt.utils.ResultMsg;
  17. import com.xiaofangtech.sunt.utils.ResultStatusCode;
  18. public class HTTPBearerAuthorizeAttribute implements Filter{
  19. @Autowired
  20. private Audience audienceEntity;
  21. @Override
  22. public void init(FilterConfig filterConfig) throws ServletException {
  23. // TODO Auto-generated method stub
  24. SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
  25. filterConfig.getServletContext());
  26. }
  27. @Override
  28. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
  29. throws IOException, ServletException {
  30. // TODO Auto-generated method stub
  31. ResultMsg resultMsg;
  32. HttpServletRequest httpRequest = (HttpServletRequest)request;
  33. String auth = httpRequest.getHeader("Authorization");
  34. if ((auth != null) && (auth.length() > 7))
  35. {
  36. String HeadStr = auth.substring(0, 6).toLowerCase();
  37. if (HeadStr.compareTo("bearer") == 0)
  38. {
  39. auth = auth.substring(7, auth.length());
  40. if (JwtHelper.parseJWT(auth, audienceEntity.getBase64Secret()) != null)
  41. {
  42. chain.doFilter(request, response);
  43. return;
  44. }
  45. }
  46. }
  47. HttpServletResponse httpResponse = (HttpServletResponse) response;
  48. httpResponse.setCharacterEncoding("UTF-8");
  49. httpResponse.setContentType("application/json; charset=utf-8");
  50. httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
  51. ObjectMapper mapper = new ObjectMapper();
  52. resultMsg = new ResultMsg(ResultStatusCode.INVALID_TOKEN.getErrcode(), ResultStatusCode.INVALID_TOKEN.getErrmsg(), null);
  53. httpResponse.getWriter().write(mapper.writeValueAsString(resultMsg));
  54. return;
  55. }
  56. @Override
  57. public void destroy() {
  58. // TODO Auto-generated method stub
  59. }
  60. }

7、在入口处注册filter

  1. package com.xiaofangtech.sunt;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import org.springframework.boot.SpringApplication;
  5. import org.springframework.boot.autoconfigure.SpringBootApplication;
  6. import org.springframework.boot.context.embedded.FilterRegistrationBean;
  7. import org.springframework.boot.context.properties.EnableConfigurationProperties;
  8. import org.springframework.context.annotation.Bean;
  9. import com.xiaofangtech.sunt.filter.HTTPBasicAuthorizeAttribute;
  10. import com.xiaofangtech.sunt.filter.HTTPBearerAuthorizeAttribute;
  11. import com.xiaofangtech.sunt.jwt.Audience;
  12. @SpringBootApplication
  13. @EnableConfigurationProperties(Audience.class)
  14. public class SpringRestApplication {
  15. public static void main(String[] args) {
  16. SpringApplication.run(SpringRestApplication.class, args);
  17. }
  18. @Bean
  19. public FilterRegistrationBean  basicFilterRegistrationBean() {
  20. FilterRegistrationBean registrationBean = new FilterRegistrationBean();
  21. HTTPBasicAuthorizeAttribute httpBasicFilter = new HTTPBasicAuthorizeAttribute();
  22. registrationBean.setFilter(httpBasicFilter);
  23. List<String> urlPatterns = new ArrayList<String>();
  24. urlPatterns.add("/user/getuser");
  25. registrationBean.setUrlPatterns(urlPatterns);
  26. return registrationBean;
  27. }
  28. @Bean
  29. public FilterRegistrationBean jwtFilterRegistrationBean(){
  30. FilterRegistrationBean registrationBean = new FilterRegistrationBean();
  31. HTTPBearerAuthorizeAttribute httpBearerFilter = new HTTPBearerAuthorizeAttribute();
  32. registrationBean.setFilter(httpBearerFilter);
  33. List<String> urlPatterns = new ArrayList<String>();
  34. urlPatterns.add("/user/getusers");
  35. registrationBean.setUrlPatterns(urlPatterns);
  36. return registrationBean;
  37. }
  38. }

8、添加获取md5的方法类MyUtils

  1. package com.xiaofangtech.sunt.utils;
  2. import java.security.MessageDigest;
  3. public class MyUtils {
  4. public static String getMD5(String inStr) {
  5. MessageDigest md5 = null;
  6. try {
  7. md5 = MessageDigest.getInstance("MD5");
  8. } catch (Exception e) {
  9. e.printStackTrace();
  10. return "";
  11. }
  12. char[] charArray = inStr.toCharArray();
  13. byte[] byteArray = new byte[charArray.length];
  14. for (int i = 0; i < charArray.length; i++)
  15. byteArray[i] = (byte) charArray[i];
  16. byte[] md5Bytes = md5.digest(byteArray);
  17. StringBuffer hexValue = new StringBuffer();
  18. for (int i = 0; i < md5Bytes.length; i++) {
  19. int val = ((int) md5Bytes[i]) & 0xff;
  20. if (val < 16)
  21. hexValue.append("0");
  22. hexValue.append(Integer.toHexString(val));
  23. }
  24. return hexValue.toString();
  25. }
  26. }

9、在返回信息类中补充添加错误码

  1. INVALID_CLIENTID(30003, "Invalid clientid"),
  2. INVALID_PASSWORD(30004, "User name or password is incorrect"),
  3. INVALID_CAPTCHA(30005, "Invalid captcha or captcha overdue"),
  4. INVALID_TOKEN(30006, "Invalid token");

10、代码中涉及的Audience类,在上一篇文章中定义,本文不再重复说明

11、代码整体结构

12、测试

1) 获取token,传入用户认证信息

认证通过返回token信息

2) 使用上面获取的token进行接口调用

未使用token,获取token错误,或者token过期时

使用正确的token时

[转] Spring Boot实战之Filter实现使用JWT进行接口认证的更多相关文章

  1. 《spring boot 实战》读书笔记

    前言:虽然已经用spring boot开发过一套系统,但是之前都是拿来主义,没有系统的,全面的了解过这套框架.现在通过学习<spring boot实战>这本书,希望温故知新.顺便实现自己的 ...

  2. spring boot实战(第一篇)第一个案例

    版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[+]   spring boot实战(第一篇)第一个案例 前言 写在前面的话 一直想将spring boot相关内容写成一个系列的 ...

  3. 《Spring Boot实战》笔记(目录)

    目录 目 录第一部分 点睛Spring 4.x第1 章 Spring 基础 .............................................................. ...

  4. 《Spring Boot 实战纪实》之过滤器

    导航 什么是过滤器 Spring的过滤器 Filter定义 过滤的对象 典型应用 过滤器的使用 Filter生命周期 过滤器链 自定义敏感词过滤器 新增自定义过滤器 添加 @WebFilter注解 添 ...

  5. spring boot实战(第十三篇)自动配置原理分析

    前言 spring Boot中引入了自动配置,让开发者利用起来更加的简便.快捷,本篇讲利用RabbitMQ的自动配置为例讲分析下Spring Boot中的自动配置原理. 在上一篇末尾讲述了Spring ...

  6. spring boot实战(第十二篇)整合RabbitMQ

    前言 最近几篇文章将围绕消息中间件RabbitMQ展开,对于RabbitMQ基本概念这里不阐述,主要讲解RabbitMQ的基本用法.Java客户端API介绍.spring Boot与RabbitMQ整 ...

  7. Spring Boot实战系列-----------邮件发送

    快速导航 添加Maven依赖 配置文件增加邮箱相关配置 Service.Test项目代码构建 五种邮件发送类型讲解 文本邮件 html邮件 附件邮件 html内嵌图片邮件 模板邮件 问题汇总 添加ma ...

  8. Spring Boot 实战与原理分析视频课程

    Spring Boot 实战与原理分析视频课程 链接:https://pan.baidu.com/share/init?surl=PeykcoeqZtd1d9lN9V_F-A 提取码: 关注公众号[G ...

  9. 9.Spring Boot实战之配置使用Logback进行日志记录

    转自:https://blog.csdn.net/meiliangdeng1990/article/details/54300227 Spring Boot实战之配置使用Logback进行日志记录 在 ...

随机推荐

  1. 关于mysql自增字段问题

    最近遇到mysql字段的自增问题,需要临时处理一下,然后就顺便补补课,这样就有了这样一篇文章. 1.自增值是什么 他是一个字段属性,是用来创建唯一标识的列的 The AUTO_INCREMENT at ...

  2. ROS naviagtion analysis: costmap_2d--CostmapLayer

    博客转自:https://blog.csdn.net/u013158492/article/details/50493220 这个类是为ObstacleLayer StaticLayer voxelL ...

  3. 第七课 ROS的空间描述和变换

    在命令行工具中也有一个与transformcaster相类似的工具叫做static_transform_publisher,它能够接受命令行参数来接受位置信息.旋转信息.父框架.子框架以及周期信息,通 ...

  4. QGIS编译教程

    注意更新时间:Thursday November 02, 2017 1. Introduction 简介 This document is the original installation guid ...

  5. 编写高质量代码改善C#程序的157个建议——建议32:总是优先考虑泛型

    建议32:总是优先考虑泛型 泛型的优点是多方面的,无论泛型类还是泛型方法都同时具备可重用性.类型安全性和高效率等特性,这是非泛型和非泛型方法无法具备的. 以可重用性为例: class MyList { ...

  6. [转]FreeMarker使用

    copy自http://demojava.iteye.com/blog/800204 以下内容全部是网上收集: FreeMarker的模板文件并不比HTML页面复杂多少,FreeMarker模板文件主 ...

  7. 操作文件方法简单总结(File,Directory,StreamReader,StreamWrite ) - Zery-zhang

    一 基本介绍 操作文档,文件夹,需要用到的类 1 Directory (静态类) :      用于创建.移动和删除等操作通过 目录 和子 目录 DirectoryInfo (非静态): 2 File ...

  8. React + Python 七月小说网 启程(一)

    一.为啥要做这个网站 很久没有写技术相关的博客了,最近几个月忙飞,各种工作,技术根本学不完,很难受. 趁着春节期间,终于有空闲时间做自己爱做的事情了,美滋滋. 热爱技术,热爱小说,于是诞生了个这么玩意 ...

  9. 【转载】C# DataGridView 通过代码设置样式

    // 表格上下左右自适应 dataGridView.Anchor = (AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom | An ...

  10. CentOS 6.9下PXE+Kickstart无人值守安装操作系统

    一.简介 1.1 什么是PXE PXE(Pre-boot Execution Environment,预启动执行环境)是由Intel公司开发的最新技术,工作于Client/Server的网络模式,支持 ...