詳解Spring Boot實(shí)戰(zhàn)之Filter實(shí)現(xiàn)使用JWT進(jìn)行接口認(rèn)證
本文介紹了spring Boot實(shí)戰(zhàn)之Filter實(shí)現(xiàn)使用JWT進(jìn)行接口認(rèn)證,分享給大家
jwt(json web token)
用戶發(fā)送按照約定,向服務(wù)端發(fā)送 Header、Payload 和 Signature,并包含認(rèn)證信息(密碼),驗(yàn)證通過(guò)后服務(wù)端返回一個(gè)token,之后用戶使用該token作為登錄憑證,適合于移動(dòng)端和api
jwt使用流程
本文示例接上面幾篇文章中的代碼進(jìn)行編寫(xiě),請(qǐng)閱讀本文的同時(shí)可以參考前面幾篇文章
1、添加依賴庫(kù)jjwt,本文中構(gòu)造jwt及解析jwt都使用了jjwt庫(kù)
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.6.0</version> </dependency>
2、添加登錄獲取token時(shí),所需要的認(rèn)證信息類(lèi)LoginPara.Java
package com.xiaofangtech.sunt.jwt; public class LoginPara { private String clientId; private String userName; private String password; private String captchaCode; private String captchaValue; public String getClientId() { return clientId; } public void setClientId(String clientId) { this.clientId = clientId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getCaptchaCode() { return captchaCode; } public void setCaptchaCode(String captchaCode) { this.captchaCode = captchaCode; } public String getCaptchaValue() { return captchaValue; } public void setCaptchaValue(String captchaValue) { this.captchaValue = captchaValue; } }
3、添加構(gòu)造jwt及解析jwt的幫助類(lèi)JwtHelper.java
package com.xiaofangtech.sunt.jwt; import java.security.Key; import java.util.Date; import javax.crypto.spec.SecretKeySpec; import javax.xml.bind.DatatypeConverter; import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; public class JwtHelper { public static Claims parseJWT(String jsonWebToken, String base64Security){ try { Claims claims = Jwts.parser() .setSigningKey(DatatypeConverter.parseBase64Binary(base64Security)) .parseClaimsJws(jsonWebToken).getBody(); return claims; } catch(Exception ex) { return null; } } public static String createJWT(String name, String userId, String role, String audience, String issuer, long TTLMillis, String base64Security) { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; long nowMillis = System.currentTimeMillis(); Date now = new Date(nowMillis); //生成簽名密鑰 byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(base64Security); Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName()); //添加構(gòu)成JWT的參數(shù) JwtBuilder builder = Jwts.builder().setHeaderParam("typ", "JWT") .claim("role", role) .claim("unique_name", name) .claim("userid", userId) .setIssuer(issuer) .setAudience(audience) .signWith(signatureAlgorithm, signingKey); //添加Token過(guò)期時(shí)間 if (TTLMillis >= 0) { long expMillis = nowMillis + TTLMillis; Date exp = new Date(expMillis); builder.setExpiration(exp).setNotBefore(now); } //生成JWT return builder.compact(); } }
4、添加token返回結(jié)果類(lèi)AccessToken.java
package com.xiaofangtech.sunt.jwt; public class AccessToken { private String access_token; private String token_type; private long expires_in; public String getAccess_token() { return access_token; } public void setAccess_token(String access_token) { this.access_token = access_token; } public String getToken_type() { return token_type; } public void setToken_type(String token_type) { this.token_type = token_type; } public long getExpires_in() { return expires_in; } public void setExpires_in(long expires_in) { this.expires_in = expires_in; } }
5、添加獲取token的接口,通過(guò)傳入用戶認(rèn)證信息(用戶名、密碼)進(jìn)行認(rèn)證獲取
package com.xiaofangtech.sunt.jwt; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.xiaofangtech.sunt.bean.UserInfo; import com.xiaofangtech.sunt.repository.UserInfoRepository; import com.xiaofangtech.sunt.utils.MyUtils; import com.xiaofangtech.sunt.utils.ResultMsg; import com.xiaofangtech.sunt.utils.ResultStatusCode; @RestController public class JsonWebToken { @Autowired private UserInfoRepository userRepositoy; @Autowired private Audience audienceEntity; @RequestMapping("oauth/token") public Object getAccessToken(@RequestBody LoginPara loginPara) { ResultMsg resultMsg; try { if(loginPara.getClientId() == null || (loginPara.getClientId().compareTo(audienceEntity.getClientId()) != 0)) { resultMsg = new ResultMsg(ResultStatusCode.INVALID_CLIENTID.getErrcode(), ResultStatusCode.INVALID_CLIENTID.getErrmsg(), null); return resultMsg; } //驗(yàn)證碼校驗(yàn)在后面章節(jié)添加 //驗(yàn)證用戶名密碼 UserInfo user = userRepositoy.findUserInfoByName(loginPara.getUserName()); if (user == null) { resultMsg = new ResultMsg(ResultStatusCode.INVALID_PASSWORD.getErrcode(), ResultStatusCode.INVALID_PASSWORD.getErrmsg(), null); return resultMsg; } else { String md5Password = MyUtils.getMD5(loginPara.getPassword()+user.getSalt()); if (md5Password.compareTo(user.getPassword()) != 0) { resultMsg = new ResultMsg(ResultStatusCode.INVALID_PASSWORD.getErrcode(), ResultStatusCode.INVALID_PASSWORD.getErrmsg(), null); return resultMsg; } } //拼裝accessToken String accessToken = JwtHelper.createJWT(loginPara.getUserName(), String.valueOf(user.getName()), user.getRole(), audienceEntity.getClientId(), audienceEntity.getName(), audienceEntity.getExpiresSecond() * 1000, audienceEntity.getBase64Secret()); //返回accessToken AccessToken accessTokenEntity = new AccessToken(); accessTokenEntity.setAccess_token(accessToken); accessTokenEntity.setExpires_in(audienceEntity.getExpiresSecond()); accessTokenEntity.setToken_type("bearer"); resultMsg = new ResultMsg(ResultStatusCode.OK.getErrcode(), ResultStatusCode.OK.getErrmsg(), accessTokenEntity); return resultMsg; } catch(Exception ex) { resultMsg = new ResultMsg(ResultStatusCode.SYSTEM_ERR.getErrcode(), ResultStatusCode.SYSTEM_ERR.getErrmsg(), null); return resultMsg; } } }
6、添加使用jwt認(rèn)證的filter
package com.xiaofangtech.sunt.filter; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.context.support.SpringBeanAutowiringSupport; import com.fasterxml.jackson.databind.ObjectMapper; import com.xiaofangtech.sunt.jwt.Audience; import com.xiaofangtech.sunt.jwt.JwtHelper; import com.xiaofangtech.sunt.utils.ResultMsg; import com.xiaofangtech.sunt.utils.ResultStatusCode; public class HTTPBearerAuthorizeAttribute implements Filter{ @Autowired private Audience audienceEntity; @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext()); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub ResultMsg resultMsg; HttpServletRequest httpRequest = (HttpServletRequest)request; String auth = httpRequest.getHeader("Authorization"); if ((auth != null) && (auth.length() > 7)) { String HeadStr = auth.substring(0, 6).toLowerCase(); if (HeadStr.compareTo("bearer") == 0) { auth = auth.substring(7, auth.length()); if (JwtHelper.parseJWT(auth, audienceEntity.getBase64Secret()) != null) { chain.doFilter(request, response); return; } } } HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setCharacterEncoding("UTF-8"); httpResponse.setContentType("application/json; charset=utf-8"); httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); ObjectMapper mapper = new ObjectMapper(); resultMsg = new ResultMsg(ResultStatusCode.INVALID_TOKEN.getErrcode(), ResultStatusCode.INVALID_TOKEN.getErrmsg(), null); httpResponse.getWriter().write(mapper.writeValueAsString(resultMsg)); return; } @Override public void destroy() { // TODO Auto-generated method stub } }
7、在入口處注冊(cè)filter
package com.xiaofangtech.sunt; import java.util.ArrayList; import java.util.List; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import com.xiaofangtech.sunt.filter.HTTPBasicAuthorizeAttribute; import com.xiaofangtech.sunt.filter.HTTPBearerAuthorizeAttribute; import com.xiaofangtech.sunt.jwt.Audience; @SpringBootApplication @EnableConfigurationProperties(Audience.class) public class SpringRestApplication { public static void main(String[] args) { SpringApplication.run(SpringRestApplication.class, args); } @Bean public FilterRegistrationBean basicFilterRegistrationBean() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(); HTTPBasicAuthorizeAttribute httpBasicFilter = new HTTPBasicAuthorizeAttribute(); registrationBean.setFilter(httpBasicFilter); List<String> urlPatterns = new ArrayList<String>(); urlPatterns.add("/user/getuser"); registrationBean.setUrlPatterns(urlPatterns); return registrationBean; } @Bean public FilterRegistrationBean jwtFilterRegistrationBean(){ FilterRegistrationBean registrationBean = new FilterRegistrationBean(); HTTPBearerAuthorizeAttribute httpBearerFilter = new HTTPBearerAuthorizeAttribute(); registrationBean.setFilter(httpBearerFilter); List<String> urlPatterns = new ArrayList<String>(); urlPatterns.add("/user/getusers"); registrationBean.setUrlPatterns(urlPatterns); return registrationBean; } }
8、添加獲取md5的方法類(lèi)MyUtils
package com.xiaofangtech.sunt.utils; import java.security.MessageDigest; public class MyUtils { public static String getMD5(String inStr) { MessageDigest md5 = null; try { md5 = MessageDigest.getInstance("MD5"); } catch (Exception e) { e.printStackTrace(); return ""; } char[] charArray = inStr.toCharArray(); byte[] byteArray = new byte[charArray.length]; for (int i = 0; i < charArray.length; i++) byteArray[i] = (byte) charArray[i]; byte[] md5Bytes = md5.digest(byteArray); StringBuffer hexValue = new StringBuffer(); for (int i = 0; i < md5Bytes.length; i++) { int val = ((int) md5Bytes[i]) & 0xff; if (val < 16) hexValue.append("0"); hexValue.append(Integer.toHexString(val)); } return hexValue.toString(); } }
9、在返回信息類(lèi)中補(bǔ)充添加錯(cuò)誤碼
INVALID_CLIENTID(30003, "Invalid clientid"), INVALID_PASSWORD(30004, "User name or password is incorrect"), INVALID_CAPTCHA(30005, "Invalid captcha or captcha overdue"), INVALID_TOKEN(30006, "Invalid token");
10、代碼中涉及的Audience類(lèi),在上一篇文章中定義,本文不再重復(fù)說(shuō)明
11、代碼整體結(jié)構(gòu)
12、測(cè)試
1) 獲取token,傳入用戶認(rèn)證信息
認(rèn)證通過(guò)返回token信息
2) 使用上面獲取的token進(jìn)行接口調(diào)用
未使用token,獲取token錯(cuò)誤,或者token過(guò)期時(shí)
使用正確的token時(shí)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
學(xué)生視角手把手帶你寫(xiě)Java?線程池初版
作者是一個(gè)來(lái)自河源的大三在校生,以下筆記都是作者自學(xué)之路的一些淺薄經(jīng)驗(yàn),如有錯(cuò)誤請(qǐng)指正,將來(lái)會(huì)不斷的完善筆記,幫助更多的Java愛(ài)好者入門(mén)2022-03-03java一個(gè)接口多個(gè)實(shí)現(xiàn)類(lèi)的調(diào)用方式
這篇文章主要給大家介紹了關(guān)于java一個(gè)接口多個(gè)實(shí)現(xiàn)類(lèi)的調(diào)用方式的相關(guān)資料,經(jīng)測(cè)試確認(rèn),當(dāng)一個(gè)接口有多個(gè)實(shí)現(xiàn)時(shí),調(diào)用時(shí)只會(huì)執(zhí)行一個(gè),有時(shí)候需要多個(gè)實(shí)現(xiàn)調(diào)用,需要的朋友可以參考下2023-09-09java Array和Arrays的區(qū)別總結(jié)
在本篇內(nèi)容里小編給大家整理的是一篇關(guān)于java Array和Arrays的區(qū)別總結(jié)內(nèi)容,有需要的朋友們可以學(xué)習(xí)下。2021-03-03Feign遠(yuǎn)程調(diào)用Multipartfile參數(shù)處理
這篇文章主要介紹了Feign遠(yuǎn)程調(diào)用Multipartfile參數(shù)處理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03java實(shí)現(xiàn)下載文件到默認(rèn)瀏覽器路徑
這篇文章主要介紹了java實(shí)現(xiàn)下載文件到默認(rèn)瀏覽器路徑,具有很好的參考價(jià)值,希望對(duì)的大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05Java微信公眾平臺(tái)開(kāi)發(fā)(14) 微信web開(kāi)發(fā)者工具使用
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)開(kāi)發(fā)第十四步,微信web開(kāi)發(fā)者工具的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04