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

来源:互联网 发布:java培训班能就业吗 编辑:程序博客网 时间:2024/04/29 03:24

      原文转载:http://blog.csdn.net/li563868273/article/details/50277359

  jwt的概念:
  http://blog.csdn.net/li563868273/article/details/50166491

  我这里主要介绍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());@AutowiredUserService userService;@ContextServletContext  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生成tokenSignatureAlgorithm 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());@AutowiredUserService userservice;//@Context//Key key;@ContextServletContext context;@Injectjavax.inject.Provider<UriInfo> uriInfo;public static String extractJwtTokenFromAuthorizationHeader(String auth) {//Replacing "Bearer Token" to "Token" directlyreturn auth.replaceFirst("[B|b][E|e][A|a][R|r][E|e][R|r] ", "").replace(" ", "");}  //重写验证过滤器@Overridepublic void filter(ContainerRequestContext containerRequestContext) throws IOException {//获取本地的私钥Key key= KeyUtil.getKey(context);//得到访问的方法 例如GET,POSTString 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;}//获取头信息中的tokenString authorizationHeader = ((ContainerRequest) containerRequestContext).getHeaderString("auth_token");//如果token为空抛出if (authorizationHeader == null) {throw new WebApplicationException(Response.Status.UNAUTHORIZED);//抛出未认证的错误}//把Bear Token换成TokenString strToken=extractJwtTokenFromAuthorizationHeader(authorizationHeader);if (TokenUtil.isValid(strToken,key)){String name=TokenUtil.getName(strToken,key);//反解出NameString[] 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角色访问。

自此实现完全完毕,demo地址https://github.com/lzggsimida123/sharebook-jersey-jwt-spring-hibernate

0 0
原创粉丝点击