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

时间:2023-03-09 22:46:23
[转] Spring Boot实战之Filter实现使用JWT进行接口认证

【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使用流程

[转] Spring Boot实战之Filter实现使用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、代码整体结构

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

12、测试

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

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

认证通过返回token信息

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

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

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

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

使用正确的token时

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