zoukankan      html  css  js  c++  java
  • Jwt在Java项目中的简单实际应用

    1.什么是jwt

    双方之间传递安全信息的简洁的、URL安全的表述性声明规范。JWT作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以Json对象的形式安全的传递信息。简洁(Compact): 可以通过URL,POST参数或者在HTTP header发送,因为数据量小,传输速度也很快 自包含(Self-contained):负载中包含了所有用户所需要的信息,避免了多次查询数据库。
     

    2.Jwt在javaweb项目中的简单使用

    第一步:引入maven依赖

    复制代码
    <!--引入JWT依赖,由于是基于Java,所以需要的是java-jwt-->
            <dependency>
                <groupId>io.jsonwebtoken</groupId>
                <artifactId>jjwt</artifactId>
                <version>0.9.1</version>
            </dependency>
            <dependency>
                <groupId>com.auth0</groupId>
                <artifactId>java-jwt</artifactId>
                <version>3.4.0</version>
            </dependency>
    复制代码

    第二步:创建两个注解,拦截器通过注释区分是否进行权限拦截(详情参考上一篇文章JWT在JAVAWEB项目中的应用核心步骤解读 https://www.cnblogs.com/jimisun/p/9480886.html)

    复制代码
    package com.pjb.springbootjjwt.jimisun;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LoginToken {
        boolean required() default true;
    }
    复制代码
    复制代码
    package com.pjb.springbootjjwt.jimisun;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CheckToken {
        boolean required() default true;
    }
    复制代码

    第三步:编写JwtUtil工具类(生成token,解析token,校验token)

    复制代码
    package com.pjb.springbootjjwt.jimisun;
    
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.UUID;
    
    /**
     * @Author:jimisun
     * @Description:
     * @Date:Created in 14:08 2018/8/15
     * @Modified By:
     */
    public class JwtUtil {
    
        /**
         * 用户登录成功后生成Jwt
         * 使用Hs256算法  私匙使用用户密码
         *
         * @param ttlMillis jwt过期时间
         * @param user      登录成功的user对象
         * @return
         */
        public static String createJWT(long ttlMillis, User user) {
            //指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。
            SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
    
            //生成JWT的时间
            long nowMillis = System.currentTimeMillis();
            Date now = new Date(nowMillis);
    
            //创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)
            Map<String, Object> claims = new HashMap<String, Object>();
            claims.put("id", user.getId());
            claims.put("username", user.getUsername());
            claims.put("password", user.getPassword());
    
            //生成签名的时候使用的秘钥secret,这个方法本地封装了的,一般可以从本地配置文件中读取,切记这个秘钥不能外露哦。它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
            String key = user.getPassword();
    
            //生成签发人
            String subject = user.getUsername();
    
    
    
            //下面就是在为payload添加各种标准声明和私有声明了
            //这里其实就是new一个JwtBuilder,设置jwt的body
            JwtBuilder builder = Jwts.builder()
                    //如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                    .setClaims(claims)
                    //设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                    .setId(UUID.randomUUID().toString())
                    //iat: jwt的签发时间
                    .setIssuedAt(now)
                    //代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。
                    .setSubject(subject)
                    //设置签名使用的签名算法和签名使用的秘钥
                    .signWith(signatureAlgorithm, key);
            if (ttlMillis >= 0) {
                long expMillis = nowMillis + ttlMillis;
                Date exp = new Date(expMillis);
                //设置过期时间
                builder.setExpiration(exp);
            }
            return builder.compact();
        }
    
    
        /**
         * Token的解密
         * @param token 加密后的token
         * @param user  用户的对象
         * @return
         */
        public static Claims parseJWT(String token, User user) {
            //签名秘钥,和生成的签名的秘钥一模一样
            String key = user.getPassword();
    
            //得到DefaultJwtParser
            Claims claims = Jwts.parser()
                    //设置签名的秘钥
                    .setSigningKey(key)
                    //设置需要解析的jwt
                    .parseClaimsJws(token).getBody();
            return claims;
        }
    
    
        /**
         * 校验token
         * 在这里可以使用官方的校验,我这里校验的是token中携带的密码于数据库一致的话就校验通过
         * @param token
         * @param user
         * @return
         */
        public static Boolean isVerify(String token, User user) {
            //签名秘钥,和生成的签名的秘钥一模一样
            String key = user.getPassword();
    
            //得到DefaultJwtParser
            Claims claims = Jwts.parser()
                    //设置签名的秘钥
                    .setSigningKey(key)
                    //设置需要解析的jwt
                    .parseClaimsJws(token).getBody();
    
            if (claims.get("password").equals(user.getPassword())) {
                return true;
            }
    
            return false;
        }
    
    }
    复制代码

    第四步:编写拦截器拦截请求进行权限验证

    复制代码
    package com.pjb.springbootjjwt.interceptor;
    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.exceptions.JWTDecodeException;
    import com.pjb.springbootjjwt.jimisun.CheckToken;
    import com.pjb.springbootjjwt.jimisun.JwtUtil;
    import com.pjb.springbootjjwt.jimisun.LoginToken;
    import com.pjb.springbootjjwt.jimisun.User;
    import com.pjb.springbootjjwt.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.lang.reflect.Method;
    
    
    /**
     * jimisun
     */
    public class AuthenticationInterceptor implements HandlerInterceptor {
    
        @Autowired
        UserService userService;
    
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
    
            // 从 http 请求头中取出 token
            String token = httpServletRequest.getHeader("token");
            // 如果不是映射到方法直接通过
            if (!(object instanceof HandlerMethod)) {
                return true;
            }
    
            HandlerMethod handlerMethod = (HandlerMethod) object;
            Method method = handlerMethod.getMethod();
            //检查是否有LoginToken注释,有则跳过认证
            if (method.isAnnotationPresent(LoginToken.class)) {
                LoginToken loginToken = method.getAnnotation(LoginToken.class);
                if (loginToken.required()) {
                    return true;
                }
            }
    
            //检查有没有需要用户权限的注解
            if (method.isAnnotationPresent(CheckToken.class)) {
                CheckToken checkToken = method.getAnnotation(CheckToken.class);
                if (checkToken.required()) {
                    // 执行认证
                    if (token == null) {
                        throw new RuntimeException("无token,请重新登录");
                    }
                    // 获取 token 中的 user id
                    String userId;
                    try {
                        userId = JWT.decode(token).getClaim("id").asString();
                    } catch (JWTDecodeException j) {
                        throw new RuntimeException("访问异常!");
                    }
                    User user = userService.findUserById(userId);
                    if (user == null) {
                        throw new RuntimeException("用户不存在,请重新登录");
                    }
                    Boolean verify = JwtUtil.isVerify(token, user);
                    if (!verify) {
                        throw new RuntimeException("非法访问!");
                    }
                    return true;
                }
            }
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    
        }
    
    
    }
    复制代码
    配置拦截器(ps:我使用的是springboot,大家使用ssm配置拦截器的方式不一样)
    复制代码
    package com.pjb.springbootjjwt.interceptorconfig;
    
    import com.pjb.springbootjjwt.interceptor.AuthenticationInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    
    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
    
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authenticationInterceptor())
                    .addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
        }
    
        @Bean
        public AuthenticationInterceptor authenticationInterceptor() {
            return new AuthenticationInterceptor();
        }
    }
    复制代码

    第五步:在示例Controller中的实际应用

    复制代码
    package com.pjb.springbootjjwt.jimisun;
    
    import com.alibaba.fastjson.JSONObject;
    import com.pjb.springbootjjwt.annotation.PassToken;
    import com.pjb.springbootjjwt.annotation.UserLoginToken;
    import com.pjb.springbootjjwt.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import javax.validation.Valid;
    import java.util.UUID;
    
    /**
     * @Author:jimisun
     * @Description:
     * @Date:Created in 15:04 2018/8/15
     * @Modified By:
     */
    @RestController
    @RequestMapping("/api")
    public class UserController {
    
        @Autowired
        private UserService userService;
    
        //登录
        @PostMapping("/login")
        @LoginToken
        public Object login(@RequestBody @Valid com.pjb.springbootjjwt.jimisun.User user) {
    
            JSONObject jsonObject = new JSONObject();
            com.pjb.springbootjjwt.jimisun.User userForBase = userService.findByUsername(user);
            if (userForBase == null) {
                jsonObject.put("message", "登录失败,用户不存在");
                return jsonObject;
            } else {
                if (!userForBase.getPassword().equals(user.getPassword())) {
                    jsonObject.put("message", "登录失败,密码错误");
                    return jsonObject;
                } else {
                    String token = JwtUtil.createJWT(6000000, userForBase);
                    jsonObject.put("token", token);
                    jsonObject.put("user", userForBase);
                    return jsonObject;
                }
            }
        }
    
        //查看个人信息
        @CheckToken
        @GetMapping("/getMessage")
        public String getMessage() {
            return "你已通过验证";
        }
    
    
    }
    复制代码
     
     

    最后一步,我们现在来访问一下啊

    先进行登录

    登录后携带token进行业务操作

    简单的Demo程序就到这里了,当然还有很多东西没有考虑到,比如jwt在集群环境下的应用等一些问题留到下回探讨啦~

    杂家不如专家,精益求精
  • 相关阅读:
    python第三周练习
    python第一周作业
    SQLite3—数据库的学习—python
    python实现跳一跳辅助的实验报告
    Python——自己的第一个网页(文件的使用)
    第一次爬虫和测试
    numpy和matplotlib使用
    Python作业———预测球队比赛成绩
    PIL库的学习
    Pytho作业——Jieba库的使用和好玩的词云
  • 原文地址:https://www.cnblogs.com/niudaxianren/p/12596478.html
Copyright © 2011-2022 走看看