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
相关文章推荐
- JavaScript、CSS、HTML 实现用户注册页面与信息校验
- js事件(Event)知识整理
- 链接<a>执行JS
- HTML5之javascript(一)
- 2015/12/12--javascript事件处理和部分高级javascript实例
- js特效-仿照html属性title写一个弹出标题样式
- 如何js编译的文件dll对于网页电话
- 跨域问题小记以及jsonp简谈
- JS开发者调查
- JSON和XML解析等区别
- js 常用小功能
- javascript下的DOM命令:css,href,arc
- javascript自定义函数
- 轻松学习JavaScript十四:JavaScript的RegExp对象(正则表达式)
- 不可理喻的JSTL标签库
- 学习js,时查看浏览器提示的错误Uncaught SyntaxError: Unexpected token [
- JS获取当前时间
- JS中获取节点的兄弟、父、子节点的各种问题
- JSON解析
- js如何创建类(封装)