您的位置:首页 > Web前端 > JavaScript

jwt(json-web-token)在rest中的实现--jersey

2015-12-12 19:48 796 查看

jwt(json-web-token)在rest中的实现–jersey

  在这里我就不介绍jwt的概念,在我的另外一篇里面有具体的介绍

  /article/7928656.html

  我这里主要介绍jwt在java的jersey中的实现,在springMVC中实现也大同小异,仿照过来就行了。

实现jwt的具体步骤

总的来说实现jwt要跟着他的验证步骤来。当然也不一定要按照这个步骤。

1.实现验证地址。来获取token。举个例子,你登陆自己的信息的时候其实就是获取这个token,以后只要这个token没有过期就不需要再获取token了,这个token也是我们以后靠他来判断是否有权限访问我们的所用应用。

2.实现过滤器。我们这里实现了过滤器来判断是否有token,以及token是否过期或者token不正确(伪造的)。在这里如果正确我们也可以把信息,比如用户id,角色等信息写入上下文,在其他操作的时候就不需要在此再数据库中查询信息了。

3.实现角色控制。在jersey或springmvc的资源类中,我们对其角色控制,我这里使用的时Jersey自带的一个角色控制类RolesAllowedDynamicFeature,来控制角色权限安全。

接下来具体讲解怎么实现(注:仅针对Jersey),先导入jwt的maven依赖包

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>


实现验证地址

一般我们采用登陆的来获取token,这里用HTTP中的POST方法,和登陆表单来实现,具体信息看代码,有详细注释

/**
* Created by lizhaoz on 2015/11/30.
*/

//~--- non-JDK imports --------------------------------------------------------

import com.lclizhao.sharebook.daomain.Token;
import com.lclizhao.sharebook.daomain.User;
import com.lclizhao.sharebook.service.UserService;
import com.lclizhao.sharebook.utils.KeyUtil;
import com.lclizhao.sharebook.utils.TokenUtil;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.springframework.beans.factory.annotation.Autowired;

//~--- JDK imports ------------------------------------------------------------

import java.util.Calendar;
import java.util.Date;

import javax.annotation.security.PermitAll;

import javax.servlet.ServletContext;

import javax.validation.constraints.NotNull;

import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

/**
* @Name:
* @Author: lizhao(作者)
* @Version: V1.00 (版本号)
* @Create Date: 2015-11-26(创建日期)
* @Description:
* 验证资源类
*/
@PermitAll()
@Path("/authentication")
public class AuthenticationResource {
private final static Logger logger = LogManager.getLogger(AuthenticationResource.class.getName());
@Autowired
UserService userService;
@Context
ServletContext  context;

/**
* author:Lizhao
* Date:15/12/12
* version:1.0
*
* @param username
* @param password
*
* @return
*/
@POST
@Produces("application/json")
@Consumes("application/x-www-form-urlencoded")

// 进入验证方法的接口
public Response authenticateUser(@NotNull
@FormParam("telphone") String username, @NotNull
@FormParam("password") String password) {

// 设置这个token的生命时间
Date expiry = getExpiryDate(30 * 24 * 60);// 30天的有效日期

// 验证账号密码是否正确
User user = authenticate(username, password);

//使用Token工具类得到token,生成的策略是利用用户的姓名,到期时间,和私钥
//我这里使用的时Key key =MacProvider.generateKey(SignatureAlgorithm.HS512);
//HS512签名算法,必须保存生成的这个key到硬盘上,不然下次会出错,因为是hash算法,所以会变
//这个私钥可以理解为一把锁孔,可以依据这个锁孔来生成钥匙也就是token,但要进入这个门必须要匹配这个锁孔
String jwtString = TokenUtil.getJWTString(username, expiry, KeyUtil.getKey(context));
//这是token的实体化类,用来返回给用户
Token  token = new Token();

token.setAuthToken(jwtString);
token.setExpires(expiry);

return Response.ok(token).build();
}

/**
* author:Lizhao
* Date:15/12/12
* version:1.0
*
* @param minutes
*
* @return
*/
private Date getExpiryDate(int minutes) {

// 根据当前日期,来得到到期日期
Calendar calendar = Calendar.getInstance();

calendar.setTime(new Date());
calendar.add(Calendar.MINUTE, minutes);

return calendar.getTime();
}

/**
* author:Lizhao
* Date:15/12/12
* version:1.0
*
* @param username
* @param password
*
* @return
*
* @throws NotAuthorizedException
* 在这个方法中实现验证用户账号密码,如果错误就抛出未验证信息,如果正确就返回一个用户
*/
private User authenticate(String username, String password) throws NotAuthorizedException {
User user = null;

user = userService.findUserByTel(username);

if (user == null) {
logger.info("Invalid username '" + username + "' ");

throw new NotAuthorizedException("Invalid telpone '" + username + "' ");
}

// we need to actually test the Hash not the password, we should never store the password in the database.
if (user.getPassword().equals(password)) {
logger.info("USER AUTHENTICATED");
} else {
logger.info("USER NOT AUTHENTICATED");

throw new NotAuthorizedException("Invalid username or password");
}

return user;
}
}


以上是验证的资源类,对于得到token的方法如下所示:

public static String getJWTString(String tel,Date expires,Key key){
if (tel == null) {
throw new NullPointerException("null username is illegal");
}

if (expires == null) {
throw new NullPointerException("null expires is illegal");
}
if (key == null) {
throw new NullPointerException("null key is illegal");
}
//用签名算法HS256和私钥key生成token
SignatureAlgorithm signatureAlgorithm =SignatureAlgorithm.HS256;
String jwtString = Jwts.builder()
.setIssuer("Jersey-Security-Basic")//设置发行人
.setSubject(tel)//设置抽象主题
.setAudience("user")//设置角色
.setExpiration(expires)//过期时间
.setIssuedAt(new Date())//设置现在时间
.setId("1")//版本1
.signWith(signatureAlgorithm,key)
.compact();
return jwtString;
}


至此我们生成了token,返回给客户端,客户端可以利用这个token在头中带上这个token,便可以进入过滤器判断是否这个token合法。

过滤器的实现

import com.lclizhao.sharebook.daomain.User;
import com.lclizhao.sharebook.service.UserService;
import com.lclizhao.sharebook.utils.KeyUtil;
import com.lclizhao.sharebook.utils.TokenUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.glassfish.jersey.server.ContainerRequest;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.Priority;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.ws.rs.Priorities;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
import java.security.Key;

/**
* @Name:
* @Author: lizhao(作者)
* @Version: V1.00 (版本号)
* @Create Date: 2015-11-26(创建日期)
* @Description:
*/
@Provider
@Priority(Priorities.AUTHENTICATION)//优先级最高
//实现该拦截器借口
//@Provider可以自动注册
public class JWTSecurityFilter implements ContainerRequestFilter{
final static Logger logger = LogManager.getLogger(JWTSecurityFilter.class.getName());
@Autowired
UserService userservice;
//@Context
//Key key;
@Context
ServletContext context;
@Inject
javax.inject.Provider<UriInfo> uriInfo;
public static String extractJwtTokenFromAuthorizationHeader(String auth) {
//Replacing "Bearer Token" to "Token" directly
return auth.replaceFirst("[B|b][E|e][A|a][R|r][E|e][R|r] ", "").replace(" ", "");
}
//重写验证过滤器
@Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
//获取本地的私钥
Key key= KeyUtil.getKey(context);
//得到访问的方法 例如GET,POST
String method = containerRequestContext.getMethod().toLowerCase();
//得到访问路径
String path = ((ContainerRequest) containerRequestContext).getPath(true).toLowerCase();
//get application.wadl和application.wadl/xsd0.xsd不需要验证,post验证过滤,注册过滤。
if (("get".equals(method) && ("application.wadl".equals(path) || "application.wadl/xsd0.xsd".equals(path)))
|| ("post".equals(method) &&( "authentication".equals(path)||"regist".equals(path)))||("get".equals(method) && "user".equals(path))) {
// pass through the filter.
containerRequestContext.setSecurityContext(new SecurityContextAuthorizer(uriInfo,new AuthorPricinple("pass"), new String[]{"pass"}));
return;
}
//获取头信息中的token
String authorizationHeader = ((ContainerRequest) containerRequestContext).getHeaderString("auth_token");
//如果token为空抛出
if (authorizationHeader == null) {

throw new WebApplicationException(Response.Status.UNAUTHORIZED);//抛出未认证的错误
}
//把Bear Token换成Token
String strToken=extractJwtTokenFromAuthorizationHeader(authorizationHeader);
if (TokenUtil.isValid(strToken,key)){
String name=TokenUtil.getName(strToken,key);//反解出Name
String[] roles=TokenUtil.getRoles(strToken,key);//反解出角色
int version=TokenUtil.getVersion(strToken,key);//得到版本
if(name !=null&&roles.length!=0&&version!=-1){
User user=userservice.findUserByTel(name);
if(user!=null){
containerRequestContext.setSecurityContext(new SecurityContextAuthorizer(uriInfo,new AuthorPricinple(name), new String[]{"user"}));
return;
}
else{
logger.info("User not found " + name);
}
}
else {
logger.info("name, roles or version missing from token");
}
}
else {
logger.info("token is invalid");

}
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
}


过滤器至此完成,总结就是:token正确就过滤,如果不正确就抛出未认证异常。

角色控制的实现

过滤器控制完成了就到了资源类的访问,首先我们要注册

register(RolesAllowedDynamicFeature.class);//角色控制

这是Jersey提供的角色访问拦截器,利用javax.annotation.security提供的来完成角色控制。

@PermitAll()
@Path("/authentication")
public class AuthenticationResource {

@RolesAllowed({"user","admin"})
public void xx
}


@PermitAll在类上注解意思是所有角色均可访问。

@RolesAllowed({“user”,”admin”}) 意思允许user角色和admin角色访问。

自此实现完全完毕,具体实现参考我的github地址https://github.com/lzggsimida123/sharebook-jersey-jwt-spring-hibernate
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: