下面我们用springmvc和jwt的类库来实现一个例子。
要使用jwt,需要pom.xml中添加如下依赖
com.auth0
java-jwt
3.2.0
下面贴一个完整的pom.xml
4.0.0
org.jstudioframework
jstudio-jwt
1.0-SNAPSHOT
UTF-8
4.12
4.3.7.RELEASE
2.8.4
3.2.0
1.2
2.5
2.2
junit
junit
${junit.version}
test
com.auth0
java-jwt
${java.jwt.version}
com.fasterxml.jackson.core
jackson-databind
${jackson.version}
com.fasterxml.jackson.core
jackson-core
${jackson.version}
org.springframework
spring-core
${spring.version}
org.springframework
spring-beans
${spring.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-webmvc
${spring.version}
javax.servlet
jstl
${jstl.version}
javax.servlet
servlet-api
${servlet-api.version}
provided
javax.servlet.jsp
jsp-api
${jsp-api.version}
provided
org.apache.tomcat.maven
tomcat7-maven-plugin
8080
/
http://127.0.0.1:8080/manager/text
tomcat
tomcat
一。定义一个jwt的工具类,具有加密和解密token的功能
importjava.util.HashMap;importjava.util.Map;/*** JwtToken*/
public classJwtToken {//密钥
private static final String SECRET = "secret";//jackson
private static ObjectMapper mapper = newObjectMapper();/*** header数据
*@return
*/
private static MapcreateHead() {
Map map = new HashMap();
map.put("typ", "JWT");
map.put("alg", "HS256");returnmap;
}/*** 生成token
*
*@paramobj 对象数据
*@parammaxAge 有效期
*@param
*@return
*/
public static String sign(T obj, long maxAge) throwsUnsupportedEncodingException, JsonProcessingException {
JWTCreator.Builder builder=JWT.create();
builder.withHeader(createHead())//header
.withSubject(mapper.writeValueAsString(obj)); //payload
if (maxAge >= 0) {long expMillis = System.currentTimeMillis() +maxAge;
Date exp= newDate(expMillis);
builder.withExpiresAt(exp);
}returnbuilder.sign(Algorithm.HMAC256(SECRET));
}/*** 解密
*@paramtoken token字符串
*@paramclassT 解密后的类型
*@param
*@return
*/
public static T unsign(String token, Class classT) throwsIOException {
JWTVerifier verifier=JWT.require(Algorithm.HMAC256(SECRET)).build();
DecodedJWT jwt=verifier.verify(token);
Date exp=jwt.getExpiresAt();if(exp!=null&&exp.after(newDate())){
String subject=jwt.getSubject();returnmapper.readValue(subject, classT);
}return null;
}
}
二。 登录时根据用户传来的username和password验证身份,如果合法,便给该用户jwt加密生成token
packagejwt.controller;importcom.fasterxml.jackson.core.JsonProcessingException;importjwt.JwtToken;importjwt.model.User;importjwt.util.ResponseEntity;importorg.springframework.stereotype.Controller;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.ResponseBody;importjavax.servlet.http.HttpServletRequest;importjava.io.UnsupportedEncodingException;/*** 用户登录*/@Controllerpublic classLoginController {
@RequestMapping(value="login", produces = "application/json; charset=utf-8")
@ResponseBodypublic ResponseEntity login(HttpServletRequest request, @RequestParam( "username") String username,
@RequestParam("password") String password) throwsUnsupportedEncodingException, JsonProcessingException {
ResponseEntity responseEntity=ResponseEntity.ok();if("admin".equals(username) && "admin".equals(password)) {//模拟用户数据,真实环境需要到数据库验证
User user = newUser();
user.setId(123456);
user.setUsername(username);//给用户jwt加密生成token
String token = JwtToken.sign(user, 60L * 1000L * 30L);//封装成对象返回给客户端
responseEntity.putDataValue("userId", user.getId());
responseEntity.putDataValue("token", token);
responseEntity.putDataValue("user", user);
}else{
responseEntity=ResponseEntity.customerError();
}returnresponseEntity;
}
}
User用户类如下
packagejwt.model;/*** User bean*/
public classUser {private longid;privateString username;privateString password;public longgetId() {returnid;
}public void setId(longid) {this.id =id;
}publicString getUsername() {returnusername;
}public voidsetUsername(String username) {this.username =username;
}publicString getPassword() {returnpassword;
}public voidsetPassword(String password) {this.password =password;
}
}
返回给用户端的一个工具类
packagejwt.util;importjava.util.HashMap;importjava.util.Map;public classResponseEntity {public static final String ERRORS_KEY = "errors";private finalString message;private final intcode;private final Map data = newHashMap();publicString getMessage() {returnmessage;
}public intgetCode() {returncode;
}public MapgetData() {returndata;
}publicResponseEntity putDataValue(String key, Object value) {
data.put(key, value);return this;
}private ResponseEntity(intcode, String message) {this.code =code;this.message =message;
}public staticResponseEntity ok() {return new ResponseEntity(200, "Ok");
}public staticResponseEntity notFound() {return new ResponseEntity(404, "Not Found");
}public staticResponseEntity badRequest() {return new ResponseEntity(400, "Bad Request");
}public staticResponseEntity forbidden() {return new ResponseEntity(403, "Forbidden");
}public staticResponseEntity unauthorized() {return new ResponseEntity(401, "unauthorized");
}public staticResponseEntity serverInternalError() {return new ResponseEntity(500, "Server Internal Error");
}public staticResponseEntity customerError() {return new ResponseEntity(1001, "Customer Error");
}
}
三。在用户登录后获得userId和token,以后用户每次请求时,都得带上这两个参数,后台拿到token后解密出userId,与用户传递过来的userId比较,如果相同,则说明用户身份合法。这里用springmvc的拦截器实现验证。
注意:测试使用Params传递参数的,建议正式环境在header里添加参数
packagejwt.interceptor;importcom.fasterxml.jackson.databind.ObjectMapper;importjwt.JwtToken;importjwt.model.User;importjwt.util.ResponseEntity;importorg.springframework.web.servlet.HandlerInterceptor;importorg.springframework.web.servlet.ModelAndView;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpServletResponse;importjava.io.PrintWriter;/*** Token验证拦截器*/
public class UserTokenInterceptor implementsHandlerInterceptor {public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throwsException {
String token= request.getParameter("token");
ResponseEntity responseData=ResponseEntity.ok();//token不存在
if (token != null) {
User user= JwtToken.unsign(token, User.class);
String userId= request.getParameter("userId");//解密token后的userId与用户传来的userId不一致,大多是因为token过期
if (user != null && userId != null && Integer.parseInt(userId) ==user.getId()) {return true;
}
}
responseData=ResponseEntity.forbidden();
response.setContentType("application/json; charset=utf-8");
ObjectMapper mapper= newObjectMapper();
String json=mapper.writeValueAsString(responseData);
PrintWriter out=response.getWriter();
out.print(json);
out.flush();
out.close();return false;
}public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throwsException {
}public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throwsException {
}
}
四。springmvc的配置
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
五。 使用Chrome插件Postman进行简单的测试
1.登录,http://localhost:8080/login
记下 token和userId
2. 测试token鉴权 http://localhost:8080/info
输入正确的userId和token
注意:测试使用Params传递参数的,建议正式环境在header里添加参数
输入错误的userId或token,在拦截器那里就被拦截了