Bootstrap

Spring Boot实战之Filter实现使用JWT进行接口认证 jwt(json web token) 用户发送按照约定,向服务端发送 Header、Payload 和 Signature,

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


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


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

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


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


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


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

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

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时

;