
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() {
}}
