├── .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 |
--------------------------------------------------------------------------------