├── .gitignore ├── README.md ├── pom.xml └── src └── main ├── java └── com │ └── joe │ ├── entity │ ├── ApiResponse.java │ └── User.java │ ├── enums │ └── ApiResponseEnum.java │ ├── interceptor │ └── TokenInterceptor.java │ ├── service │ ├── IUserService.java │ └── impl │ │ └── UserServiceImpl.java │ ├── util │ ├── ApiResponseUtil.java │ └── JwtUtil.java │ └── web │ ├── LoginController.java │ └── UserController.java └── webapp ├── WEB-INF ├── applicationContext.xml ├── spring-mvc.xml └── web.xml └── index.jsp /.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | /target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | 20 | ### NetBeans ### 21 | /nbproject/private/ 22 | /nbbuild/ 23 | /dist/ 24 | /nbdist/ 25 | /.nb-gradle/ 26 | /build/ 27 | 28 | ### VS Code ### 29 | .vscode/ 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spring-token 2 | 整合JWT,spring,springMVC,实现基于token验证 3 | 4 | 因为CSDN博客上 https://blog.csdn.net/KKKun_Joe/article/details/81878231 这篇文章好多小伙伴希望我分享一下源码, 5 | 我这边就这个功能单独拉出来写了一个小示例供大家分享。 6 | 7 | 因为这篇博客讲的是基于JWT和spring,springMVC的整合,所以如果有小伙伴想整合JWT和spring-boot,只需要将spring基于xml文件的拦截器配置方式转换成 8 | spring-boot的配置方式就ok了。 9 | 10 | 欢迎大家讨论,分享! 11 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | com.joe 8 | spring-token 9 | 1.0-SNAPSHOT 10 | war 11 | 12 | spring-token Maven Webapp 13 | 14 | http://www.example.com 15 | 16 | 17 | UTF-8 18 | 1.7 19 | 1.7 20 | 21 | 22 | 23 | 24 | junit 25 | junit 26 | 4.11 27 | test 28 | 29 | 30 | 31 | javax.servlet 32 | javax.servlet-api 33 | 3.1.0 34 | 35 | 36 | javax.servlet.jsp 37 | jsp-api 38 | 2.2 39 | 40 | 41 | javax.servlet 42 | jstl 43 | 1.2 44 | 45 | 46 | 47 | 48 | org.springframework 49 | spring-web 50 | 4.2.6.RELEASE 51 | 52 | 53 | 54 | org.springframework 55 | spring-webmvc 56 | 4.2.6.RELEASE 57 | 58 | 59 | 60 | org.springframework 61 | spring-context 62 | 4.2.6.RELEASE 63 | 64 | 65 | 66 | org.springframework 67 | spring-test 68 | 4.2.6.RELEASE 69 | 70 | 71 | 72 | com.auth0 73 | java-jwt 74 | 3.3.0 75 | 76 | 77 | 78 | com.alibaba 79 | fastjson 80 | 1.2.46 81 | 82 | 83 | 84 | 85 | spring-token 86 | 87 | 88 | 89 | maven-clean-plugin 90 | 3.1.0 91 | 92 | 93 | 94 | maven-resources-plugin 95 | 3.0.2 96 | 97 | 98 | maven-compiler-plugin 99 | 3.8.0 100 | 101 | 102 | maven-surefire-plugin 103 | 2.22.1 104 | 105 | 106 | maven-war-plugin 107 | 3.2.2 108 | 109 | 110 | maven-install-plugin 111 | 2.5.2 112 | 113 | 114 | maven-deploy-plugin 115 | 2.8.2 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /src/main/java/com/joe/entity/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package com.joe.entity; 2 | 3 | import com.joe.enums.ApiResponseEnum; 4 | 5 | /** 6 | * web层统一返回类型 7 | * @author qiaokun 8 | * @date 2018/07/18 9 | */ 10 | public class ApiResponse { 11 | private int errCode = 0; 12 | 13 | private String errMsg; 14 | 15 | private Object data; 16 | 17 | public ApiResponse(){ 18 | 19 | } 20 | 21 | 22 | public ApiResponse(Object data) { 23 | this.data = data; 24 | } 25 | 26 | public ApiResponse(ApiResponseEnum apiResponseEnum){ 27 | this.errCode = apiResponseEnum.getErrCode(); 28 | this.errMsg = apiResponseEnum.getErrMsg(); 29 | } 30 | 31 | public int getErrCode() { 32 | return errCode; 33 | } 34 | 35 | public void setErrCode(int errCode) { 36 | this.errCode = errCode; 37 | } 38 | 39 | public String getErrMsg() { 40 | return errMsg; 41 | } 42 | 43 | public void setErrMsg(String errMsg) { 44 | this.errMsg = errMsg; 45 | } 46 | 47 | public Object getData() { 48 | return data; 49 | } 50 | 51 | public void setData(Object data) { 52 | this.data = data; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "ApiResponse{" + 58 | "errCode=" + errCode + 59 | ", errMsg='" + errMsg + '\'' + 60 | ", data=" + data + 61 | '}'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/joe/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.joe.entity; 2 | 3 | /** 4 | * @author:joe 5 | * @date:2019/5/20 19:38 6 | */ 7 | public class User { 8 | 9 | private String id; 10 | 11 | private String name; 12 | 13 | private Integer age; 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | public void setName(String name) { 20 | this.name = name; 21 | } 22 | 23 | public Integer getAge() { 24 | return age; 25 | } 26 | 27 | public void setAge(Integer age) { 28 | this.age = age; 29 | } 30 | 31 | public String getId() { 32 | return id; 33 | } 34 | 35 | public void setId(String id) { 36 | this.id = id; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/joe/enums/ApiResponseEnum.java: -------------------------------------------------------------------------------- 1 | package com.joe.enums; 2 | 3 | /** 4 | * web 层返回信息枚举 5 | * @author qiaokun 6 | * @2018/08/13 7 | */ 8 | public enum ApiResponseEnum { 9 | /** 10 | * API调用成功返回 11 | */ 12 | SUCCESS(10000,"请求成功"), 13 | FAIL(10001,"请求失败"), 14 | LOGIN_FAIL(10099,"登陆失败"), 15 | AUTH_ERROR(10100,"认证失败"); 16 | 17 | private int errCode = 0; 18 | 19 | private String errMsg; 20 | 21 | 22 | private ApiResponseEnum(int errCode, String errMsg) { 23 | this.errCode = errCode; 24 | this.errMsg = errMsg; 25 | } 26 | 27 | public int getErrCode() { 28 | return errCode; 29 | } 30 | 31 | public void setErrCode(int errCode) { 32 | this.errCode = errCode; 33 | } 34 | 35 | public String getErrMsg() { 36 | return errMsg; 37 | } 38 | 39 | public void setErrMsg(String errMsg) { 40 | this.errMsg = errMsg; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/joe/interceptor/TokenInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.joe.interceptor; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.joe.entity.ApiResponse; 5 | import com.joe.enums.ApiResponseEnum; 6 | import com.joe.util.ApiResponseUtil; 7 | import com.joe.util.JwtUtil; 8 | import org.springframework.web.servlet.HandlerInterceptor; 9 | import org.springframework.web.servlet.ModelAndView; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.io.PrintWriter; 14 | 15 | /** 16 | * 自定义token拦截器 17 | * 18 | * @author qiaokun 19 | * @date 2018/08/11 20 | */ 21 | public class TokenInterceptor implements HandlerInterceptor { 22 | 23 | @Override 24 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 25 | response.setCharacterEncoding("utf-8"); 26 | String token = request.getHeader("access_token"); 27 | //token不存在 28 | if (null != token) { 29 | //验证token是否正确 30 | boolean result = JwtUtil.verify(token); 31 | if (result) { 32 | return true; 33 | } 34 | } 35 | ApiResponse apiResponse = ApiResponseUtil.getApiResponse(ApiResponseEnum.AUTH_ERROR); 36 | responseMessage(response,response.getWriter(),apiResponse); 37 | return false; 38 | } 39 | 40 | @Override 41 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 42 | 43 | } 44 | 45 | @Override 46 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 47 | 48 | } 49 | 50 | /** 51 | * 返回信息给客户端 52 | * 53 | * @param response 54 | * @param out 55 | * @param apiResponse 56 | */ 57 | private void responseMessage(HttpServletResponse response, PrintWriter out, ApiResponse apiResponse) { 58 | response.setContentType("application/json; charset=utf-8"); 59 | out.print(JSONObject.toJSONString(apiResponse)); 60 | out.flush(); 61 | out.close(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/joe/service/IUserService.java: -------------------------------------------------------------------------------- 1 | package com.joe.service; 2 | 3 | import com.joe.entity.User; 4 | 5 | /** 6 | * @author:joe 7 | * @date:2019/5/20 19:43 8 | */ 9 | public interface IUserService { 10 | boolean checkUser(String loginName, String password); 11 | 12 | User getUserByLoginName(String loginName); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/joe/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.joe.service.impl; 2 | 3 | import com.joe.entity.User; 4 | import com.joe.service.IUserService; 5 | import org.springframework.stereotype.Service; 6 | 7 | /** 8 | * @author:joe 9 | * @date:2019/5/20 19:44 10 | */ 11 | @Service 12 | public class UserServiceImpl implements IUserService { 13 | 14 | @Override 15 | public boolean checkUser(String loginName, String password) { 16 | 17 | return true; 18 | } 19 | 20 | @Override 21 | public User getUserByLoginName(String loginName) { 22 | User user = new User(); 23 | user.setId("2019"); 24 | user.setName("joe"); 25 | user.setAge(18); 26 | return user; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/joe/util/ApiResponseUtil.java: -------------------------------------------------------------------------------- 1 | package com.joe.util; 2 | 3 | import com.joe.entity.ApiResponse; 4 | import com.joe.enums.ApiResponseEnum; 5 | 6 | /** 7 | * web层统一返回工具类 8 | * @author qiaokun 9 | * @date 2018/07/18 10 | */ 11 | public class ApiResponseUtil { 12 | 13 | /** 14 | * 获取请求成功响应的ApiResponse 15 | * @param data 16 | * @return 17 | */ 18 | public static ApiResponse getApiResponse(Object data) { 19 | return getApiResponse(data, ApiResponseEnum.SUCCESS.getErrCode(), ApiResponseEnum.SUCCESS.getErrMsg()); 20 | } 21 | 22 | /** 23 | * 获取其他请求响应的ApiResponse 24 | * @param code 25 | * @param msg 26 | * @return 27 | */ 28 | public static ApiResponse getApiResponse(int code,String msg) { 29 | return getApiResponse(null, code, msg); 30 | } 31 | 32 | /** 33 | * 枚举信息转统一返回对象 34 | * @param apiResponseEnum 35 | * @return 36 | */ 37 | public static ApiResponse getApiResponse(ApiResponseEnum apiResponseEnum){ 38 | return getApiResponse(apiResponseEnum.getErrCode(),apiResponseEnum.getErrMsg()); 39 | } 40 | 41 | 42 | public static ApiResponse getApiResponse(Object data, int code, String msg) { 43 | ApiResponse apiResponse = new ApiResponse(data); 44 | apiResponse.setErrCode(code); 45 | apiResponse.setErrMsg(msg); 46 | return apiResponse; 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/joe/util/JwtUtil.java: -------------------------------------------------------------------------------- 1 | package com.joe.util; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.JWTVerifier; 5 | import com.auth0.jwt.algorithms.Algorithm; 6 | import com.auth0.jwt.exceptions.JWTDecodeException; 7 | import com.auth0.jwt.interfaces.DecodedJWT; 8 | import com.fasterxml.jackson.annotation.ObjectIdGenerators; 9 | 10 | import java.io.UnsupportedEncodingException; 11 | import java.util.Date; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | /** 16 | * Java web token 工具类 17 | * 18 | * @author qiaokun 19 | * @date 2018/08/10 20 | */ 21 | public class JwtUtil { 22 | /** 23 | * 过期时间一天, 24 | * TODO 正式运行时修改为15分钟 25 | */ 26 | private static final long EXPIRE_TIME = 24 * 60 * 60 * 1000; 27 | /** 28 | * token私钥 29 | */ 30 | private static final String TOKEN_SECRET = "f26e587c28064d0e855e72c0a6a0e618"; 31 | 32 | /** 33 | * 校验token是否正确 34 | * 35 | * @param token 密钥 36 | * @return 是否正确 37 | */ 38 | public static boolean verify(String token) { 39 | try { 40 | Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); 41 | JWTVerifier verifier = JWT.require(algorithm) 42 | .build(); 43 | DecodedJWT jwt = verifier.verify(token); 44 | return true; 45 | } catch (Exception exception) { 46 | return false; 47 | } 48 | } 49 | 50 | /** 51 | * 获得token中的信息无需secret解密也能获得 52 | * 53 | * @return token中包含的用户名 54 | */ 55 | public static String getUsername(String token) { 56 | try { 57 | DecodedJWT jwt = JWT.decode(token); 58 | return jwt.getClaim("loginName").asString(); 59 | } catch (JWTDecodeException e) { 60 | return null; 61 | } 62 | } 63 | 64 | /** 65 | * 获取登陆用户ID 66 | * @param token 67 | * @return 68 | */ 69 | public static String getUserId(String token) { 70 | try { 71 | DecodedJWT jwt = JWT.decode(token); 72 | return jwt.getClaim("userId").asString(); 73 | } catch (JWTDecodeException e) { 74 | return null; 75 | } 76 | } 77 | 78 | /** 79 | * 生成签名,15min后过期 80 | * 81 | * @param username 用户名 82 | * @return 加密的token 83 | */ 84 | public static String sign(String username,String userId) { 85 | try { 86 | // 过期时间 87 | Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); 88 | // 私钥及加密算法 89 | Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); 90 | // 设置头部信息 91 | Map header = new HashMap<>(2); 92 | header.put("typ", "JWT"); 93 | header.put("alg", "HS256"); 94 | // 附带username,userId信息,生成签名 95 | return JWT.create() 96 | .withHeader(header) 97 | .withClaim("loginName", username) 98 | .withClaim("userId",userId) 99 | .withExpiresAt(date) 100 | .sign(algorithm); 101 | } catch (UnsupportedEncodingException e) { 102 | return null; 103 | } 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/com/joe/web/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.joe.web; 2 | 3 | import com.joe.entity.ApiResponse; 4 | import com.joe.entity.User; 5 | import com.joe.enums.ApiResponseEnum; 6 | import com.joe.service.IUserService; 7 | import com.joe.util.ApiResponseUtil; 8 | import com.joe.util.JwtUtil; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestMethod; 14 | import org.springframework.web.bind.annotation.ResponseBody; 15 | 16 | import java.util.Map; 17 | 18 | /** 19 | * @author:joe 20 | * @date:2019/5/20 19:40 21 | */ 22 | @Controller 23 | @RequestMapping("/") 24 | public class LoginController { 25 | 26 | @Autowired 27 | private IUserService userService; 28 | 29 | /** 30 | * 登陆接口 31 | * 32 | * @return token 33 | */ 34 | @RequestMapping(value = "/login", method = RequestMethod.POST) 35 | @ResponseBody 36 | public ApiResponse login(@RequestBody Map map) { 37 | String loginName = map.get("loginName"); 38 | String password = map.get("password"); 39 | //身份验证是否成功 40 | boolean isSuccess = userService.checkUser(loginName, password); 41 | if (isSuccess) { 42 | User user = userService.getUserByLoginName(loginName); 43 | if (user != null) { 44 | //返回token 45 | String token = JwtUtil.sign(user.getName(), user.getId()); 46 | if (token != null) { 47 | return ApiResponseUtil.getApiResponse(token); 48 | } 49 | } 50 | } 51 | //返回登陆失败消息 52 | return ApiResponseUtil.getApiResponse(ApiResponseEnum.LOGIN_FAIL); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/joe/web/UserController.java: -------------------------------------------------------------------------------- 1 | package com.joe.web; 2 | 3 | import com.joe.entity.ApiResponse; 4 | import com.joe.service.IUserService; 5 | import com.joe.util.ApiResponseUtil; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestMethod; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | 12 | /** 13 | * @author:joe 14 | * @date:2019/5/20 19:38 15 | */ 16 | @Controller 17 | @RequestMapping("/user") 18 | public class UserController { 19 | 20 | @Autowired 21 | private IUserService userService; 22 | 23 | @RequestMapping(value = "/",method = RequestMethod.GET) 24 | @ResponseBody 25 | public ApiResponse getUser(){ 26 | return ApiResponseUtil.getApiResponse(userService.getUserByLoginName("")); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/applicationContext.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/spring-mvc.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 9 | 10 | Archetype Created Web Application 11 | 12 | 13 | 14 | index.jsp 15 | 16 | 17 | 18 | 19 | springMVC 20 | org.springframework.web.servlet.DispatcherServlet 21 | 22 | 23 | contextConfigLocation 24 | /WEB-INF/spring-mvc.xml 25 | 26 | 1 27 | true 28 | 29 | 30 | springMVC 31 | / 32 | 33 | 34 | 35 | contextConfigLocation 36 | /WEB-INF/applicationContext.xml 37 | 38 | 39 | org.springframework.web.context.ContextLoaderListener 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 |

Hello World!

4 | 5 | 6 | --------------------------------------------------------------------------------