
1.pom.xml
<!-- jwt 庫相關依賴. -->
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<!-- 使用第三方json 解析框架-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.15</version>
</dependency>
<!-- 是Apache開源組織提供的用於摘要運算、編碼的包 -->
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.10</version>
</dependency>
<!-- mobile的模塊可以區分訪問主體是手機、平板、還是PC -->
<dependency>
<groupId>org.springframework.mobile</groupId>
<artifactId>spring-mobile-device</artifactId>
</dependency>
2.application.properties
server.context-path=/jwt jwt.header= Authorization jwt.secret= mySecret #設置過期時間 jwt.expiration= 604800 server.tomcat.max-http-header-size=3145728 #logging.level.root=debug
3.Application.java
package com.guilf;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import java.util.ArrayList;
import java.util.List;
@SpringBootApplication
@ServletComponentScan
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
/**
* 1.需要定義一個convert轉換消息的對象
* 2.創建配置信息,加入配置信息:比如是否需要格式化返回的json
* 3.converter中添加配置信息
* 4.convert添加到converters當中
*/
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
// 1.需要定義一個convert轉換消息的對象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
//2.創建配置信息,加入配置信息:比如是否需要格式化返回的json
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
//3.converter中添加配置信息
fastConverter.setFastJsonConfig(fastJsonConfig);
/**
* 設置json 返回格式和編碼方式 處理亂碼問題
*/
List<MediaType> mediaTypeList=new ArrayList<>();
mediaTypeList.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(mediaTypeList);
//4.convert添加到converters當中
HttpMessageConverter<?> converter = fastConverter;
return new HttpMessageConverters(converter);
}
}
4.JwtHelper.java
package com.guilf.jwt.utils;
import com.guilf.jwt.domain.LoginInfo;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mobile.device.Device;
import org.springframework.stereotype.Component;
import javax.xml.bind.DatatypeConverter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
* jwt及解析jwt的幫助類.
*
*/
@Component
public class JwtHelper {
static final String CLAIM_KEY_USERNAME = "sub";
static final String CLAIM_KEY_AUDIENCE = "audience";
static final String CLAIM_KEY_CREATED = "created";
private static final String AUDIENCE_UNKNOWN = "unknown";
private static final String AUDIENCE_WEB = "web";
private static final String AUDIENCE_MOBILE = "mobile";
private static final String AUDIENCE_TABLET = "tablet";
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private Long expiration;
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
public Long getExpiration() {
return expiration;
}
public void setExpiration(Long expiration) {
this.expiration = expiration;
}
public Claims getClaimsFromToken(String token){
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
claims = null;
}
return claims;
}
/**
* 生成token.
* @param username 用戶名
* @param device org.springframework.mobile.device 設備判斷對象
* @return
*/
public String createJWT(String username, Device device) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, username);
claims.put(CLAIM_KEY_AUDIENCE, generateAudience(device));
claims.put(CLAIM_KEY_CREATED, new Date());
return generateToken(claims);
}
private String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(generateExpirationDate())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
/**
* 生成token 時間 = 當前時間+ expiration(properties中配置的失效時間)
* @return
*/
private Date generateExpirationDate() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
/**
* 通過spring-mobile-device 的device 檢測訪問主體.
* @param device
* @return
*/
private String generateAudience(Device device) {
String audience = AUDIENCE_UNKNOWN;
if (device.isNormal()) {
audience = AUDIENCE_WEB;//Pc端
} else if (device.isTablet()) {
audience = AUDIENCE_TABLET;//Pc端
} else if (device.isMobile()) {
audience = AUDIENCE_MOBILE;//平板
}
return audience;
}
/**
* 通過token 獲取用戶名.
* @param authToken
* @return
*/
public String getUsernameFromToken(String authToken) {
String username;
try {
final Claims claims = getClaimsFromToken(authToken);
username = claims.getSubject();
} catch (Exception e) {
username = null;
}
return username;
}
/**
* 判斷token 失效時間是否到了
* @param token
* @return
*/
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
/**
* 獲取設置的token 失效時間.
* @param token
* @return
*/
public Date getExpirationDateFromToken(String token) {
Date expiration;
try {
final Claims claims = getClaimsFromToken(token);
expiration = claims.getExpiration();
} catch (Exception e) {
expiration = null;
}
return expiration;
}
/**
* Token失效校驗.
* @param token token字符串
* @param loginInfo 用戶信息
* @return
*/
public Boolean validateToken(String token, LoginInfo loginInfo) {
//1.校驗簽名是否正確
//2.token是否過期
//......
final String username = getUsernameFromToken(token);
return (
username.equals(loginInfo.getUsername())
&& !isTokenExpired(token));
}
}
5 MyMD5Util.java
package com.guilf.jwt.utils;
import org.apache.commons.codec.binary.Hex;
import org.springframework.util.StringUtils;
import java.security.MessageDigest;
import java.util.Random;
/**
* 獲取md5 加密后字符串.
*
*/
public class MyMD5Util {
/**
* 普通MD5
*
* @param password
* @return
*
*/
public static String MD5(String password) {
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (Exception e) {
e.printStackTrace();
return "";
}
char[] charArray = password.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();
}
/**
* 加鹽MD5
*
* @param password
* @return
* @author
*/
public static String MD5Salt(String password) {
Random r = new Random();
StringBuilder sb = new StringBuilder(16);
sb.append(r.nextInt(99999999)).append(r.nextInt(99999999));
int len = sb.length();
if (len < 16) {
for (int i = 0; i < 16 - len; i++) {
sb.append("0");
}
}
String salt = sb.toString();
password = md5Hex(password + salt);
char[] cs = new char[48];
for (int i = 0; i < 48; i += 3) {
cs[i] = password.charAt(i / 3 * 2);
char c = salt.charAt(i / 3);
cs[i + 1] = c;
cs[i + 2] = password.charAt(i / 3 * 2 + 1);
}
return new String(cs);
}
/**
* 校驗加鹽后是否和原文一致
*
* @param password
* @param md5
* @return
* @author
*/
public static boolean verify(String password, String md5) {
char[] cs1 = new char[32];
char[] cs2 = new char[16];
for (int i = 0; i < 32; i += 3) {
cs1[i / 3 * 2] = md5.charAt(i);
cs1[i / 3 * 2 + 1] = md5.charAt(i + 2);
cs2[i / 3] = md5.charAt(i + 1);
}
String salt = new String(cs2);
return md5Hex(password + salt).equals(new String(cs1));
}
/**
* 獲取十六進制字符串形式的MD5摘要
*/
private static String md5Hex(String src) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bs = md5.digest(src.getBytes());
return new String(new Hex().encode(bs));
} catch (Exception e) {
return null;
}
}
}
6.登錄進入 JsonWebTokenController.java
package com.hong.jwt.web;
import com.alibaba.fastjson.JSONObject;
import com.hong.jwt.domain.AccessToken;
import com.hong.jwt.domain.LoginInfo;
import com.hong.jwt.neum.UserInfoEnum;
import com.hong.jwt.utils.JwtHelper;
import com.hong.jwt.utils.MyMD5Util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mobile.device.Device;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
*/
@RestController
@RequestMapping("/oauth")
public class JsonWebTokenController {
@Autowired
private JwtHelper jwtHelper;
@RequestMapping(value = "/token")
public JSONObject getAccessToken(HttpServletResponse response, LoginInfo loginInfo, Device device) {
JSONObject resultMsg = new JSONObject();
try {
if (loginInfo.getClientId() == null) {
resultMsg.put("errcode", 500);
resultMsg.put("msg", "not is ClientId!");
return resultMsg;
}
//驗證碼校驗在后面章節添加
//驗證用戶名密碼
//1.驗證用戶是否存在.
if (!UserInfoEnum.TEST.getUsername().equals(loginInfo.getUsername()) &&
!UserInfoEnum.ADMIN.getUsername().equals(loginInfo.getUsername()) &&
!UserInfoEnum.ZHANGSAN.getUsername().equals(loginInfo.getUsername())) {
resultMsg.put("result_code", 500);
resultMsg.put("msg", "no user!");
return resultMsg;
}
//2.用戶存在,驗證密碼.
else {
// 獲取用戶登錄md5加密后的密碼.
String loginMD5Password = MyMD5Util.MD5(loginInfo.getPassword());
// 獲取匹配的密碼.
String password = null;
switch (loginInfo.getUsername()) {
case "admin":
password = UserInfoEnum.ADMIN.getPassword();
break;
case "test":
password = UserInfoEnum.TEST.getPassword();
break;
case "zhangsan":
password = UserInfoEnum.ZHANGSAN.getPassword();
break;
}
String userMD5Password = MyMD5Util.MD5(password);
if (!loginMD5Password.equals(userMD5Password)) {
resultMsg.put("result_code", 500);
resultMsg.put("msg", "password error!");
return resultMsg;
}
}
//拼裝accessToken
String accessToken = jwtHelper.createJWT(loginInfo.getUsername(), device);
//返回accessToken
AccessToken accessTokenEntity = new AccessToken();
accessTokenEntity.setAccessToken(accessToken);
accessTokenEntity.setExpiresIn(jwtHelper.getExpiration());
accessTokenEntity.setTokenType("bearer");
resultMsg.put("result_code", 200);
resultMsg.put("msg", "success");
resultMsg.put("token", accessTokenEntity);
//設置cookie
Cookie cookie =new Cookie("token",accessToken);
cookie.setHttpOnly(true);
//注:Domain為設置Cookie的有效域,Path限制有效路徑
//1、必須是1-9、a-z、A-Z、. 、- (注意是-不是_)這幾個字符組成
//2、必須是數字或字母開頭
//3、必須是數字或字母結尾
//cookie.setDomain("jwt");
response.addCookie(cookie);
return resultMsg;
} catch (Exception ex) {
System.out.println(ex);
resultMsg.put("result_code", 500);
resultMsg.put("msg", "other error!");
return resultMsg;
}
}
}
7.在進入其他控制層的時候就會進入過濾器
JwtTokenFilter.java
package com.hong.jwt.filter;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hong.jwt.domain.LoginInfo;
import com.hong.jwt.neum.UserInfoEnum;
import com.hong.jwt.utils.JwtHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
*/
@WebFilter(filterName = "jwtTokenFilter", urlPatterns = {"/oauth/user"})
public class JwtTokenFilter implements Filter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private JwtHelper jwtHelper;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
// String authToken = httpRequest.getHeader("Authorization");
//從cookie 中獲取jwt token
Cookie[] cookies = httpRequest.getCookies();
String authToken =null;
for (Cookie cookie : cookies) {
if("token".equals(cookie.getName())){
authToken = cookie.getValue();
}
}
String username = jwtHelper.getUsernameFromToken(authToken);
logger.info("checking authentication für user " + username);
if (username != null) {
// 這一步通常去數據庫獲取用戶信息
LoginInfo loginInfo =new LoginInfo();
switch (username) {
case "admin":
loginInfo.setId(UserInfoEnum.ADMIN.getId());
loginInfo.setUsername(UserInfoEnum.ADMIN.getUsername());
loginInfo.setPassword(UserInfoEnum.ADMIN.getPassword());
break;
case "test":
loginInfo.setId(UserInfoEnum.TEST.getId());
loginInfo.setUsername(UserInfoEnum.TEST.getUsername());
loginInfo.setPassword(UserInfoEnum.TEST.getPassword());
break;
case "zhangsan":
loginInfo.setId(UserInfoEnum.ZHANGSAN.getId());
loginInfo.setUsername(UserInfoEnum.ZHANGSAN.getUsername());
loginInfo.setPassword(UserInfoEnum.ZHANGSAN.getPassword());
break;
}
// 驗證 token 有效性.
if (jwtHelper.validateToken(authToken,loginInfo)) {
filterChain.doFilter(servletRequest,servletResponse);
}
// 驗證token 失敗.
resultWrite((HttpServletResponse) servletResponse);
return;
}else{
// 驗證token 失敗.
resultWrite((HttpServletResponse) servletResponse);
return;
}
}
private void resultWrite(HttpServletResponse servletResponse) throws IOException {
HttpServletResponse httpResponse = servletResponse;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json; charset=utf-8");
httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ObjectMapper mapper = new ObjectMapper();
JSONObject resultMsg =new JSONObject();
resultMsg.put("result_code", 500);
resultMsg.put("msg", "authentication failure!");
httpResponse.getWriter().write(mapper.writeValueAsString(resultMsg));
}
@Override
public void destroy() {
}
}
8.UserController.java
package com.hong.jwt.web;
import com.alibaba.fastjson.JSONObject;
import com.hong.jwt.neum.UserInfoEnum;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
*/
@RestController
public class UserController {
@PostMapping("/user")
public JSONObject user(int id){
JSONObject resultMsg =new JSONObject();
switch (id) {
case 1:
resultMsg.put("username",UserInfoEnum.TEST.getUsername());
break;
case 2:
resultMsg.put("username",UserInfoEnum.ADMIN.getUsername());
break;
case 3:
resultMsg.put("username",UserInfoEnum.ZHANGSAN.getUsername());
break;
}
return resultMsg;
}
}
package com.hong.jwt.filter;
import com.alibaba.fastjson.JSONObject;import com.fasterxml.jackson.databind.ObjectMapper;import com.hong.jwt.domain.LoginInfo;import com.hong.jwt.neum.UserInfoEnum;import com.hong.jwt.utils.JwtHelper;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.*;import javax.servlet.annotation.WebFilter;import javax.servlet.http.Cookie;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;
/** * */@WebFilter(filterName = "jwtTokenFilter", urlPatterns = {"/oauth/user"})public class JwtTokenFilter implements Filter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired private JwtHelper jwtHelper;
@Override public void init(FilterConfig filterConfig) throws ServletException {
}
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
// String authToken = httpRequest.getHeader("Authorization");
//從cookie 中獲取jwt token Cookie[] cookies = httpRequest.getCookies(); String authToken =null; for (Cookie cookie : cookies) { if("token".equals(cookie.getName())){ authToken = cookie.getValue(); } } String username = jwtHelper.getUsernameFromToken(authToken);
logger.info("checking authentication für user " + username);
if (username != null) {
// 這一步通常去數據庫獲取用戶信息 LoginInfo loginInfo =new LoginInfo(); switch (username) { case "admin": loginInfo.setId(UserInfoEnum.ADMIN.getId()); loginInfo.setUsername(UserInfoEnum.ADMIN.getUsername()); loginInfo.setPassword(UserInfoEnum.ADMIN.getPassword()); break; case "test": loginInfo.setId(UserInfoEnum.TEST.getId()); loginInfo.setUsername(UserInfoEnum.TEST.getUsername()); loginInfo.setPassword(UserInfoEnum.TEST.getPassword()); break; case "zhangsan": loginInfo.setId(UserInfoEnum.ZHANGSAN.getId()); loginInfo.setUsername(UserInfoEnum.ZHANGSAN.getUsername()); loginInfo.setPassword(UserInfoEnum.ZHANGSAN.getPassword()); break; }
// 驗證 token 有效性. if (jwtHelper.validateToken(authToken,loginInfo)) { filterChain.doFilter(servletRequest,servletResponse); }
// 驗證token 失敗. resultWrite((HttpServletResponse) servletResponse); return; }else{ // 驗證token 失敗. resultWrite((HttpServletResponse) servletResponse); return; } }
private void resultWrite(HttpServletResponse servletResponse) throws IOException { HttpServletResponse httpResponse = servletResponse; httpResponse.setCharacterEncoding("UTF-8"); httpResponse.setContentType("application/json; charset=utf-8"); httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ObjectMapper mapper = new ObjectMapper(); JSONObject resultMsg =new JSONObject(); resultMsg.put("result_code", 500); resultMsg.put("msg", "authentication failure!"); httpResponse.getWriter().write(mapper.writeValueAsString(resultMsg)); }
@Override public void destroy() {
}}
