jwt(json web token)使用demo

jwt的用处:一般用于用户认证,前后端分离的项目,微信小程序,app 等,可以说只要是要登录的都要用到,具体的作用就是生成token,将token返回给浏览器,在用户在下次登录时带回token会来,服务端再对token进行校验,支持实现了跨域请求。

先说下普通跨域身份验证
过程如下:
用户向服务器发送请求包含用户名和密码。验证服务器后,相关数据(如用户信息等)将保存在当前会话中。服务器向用户返回session_id,session信息都会写入到用户的Cookie。用户的每个请求通过在Cookie中取出session_id传给服务器。服务器收到session_id并对比之前保存的数据,确认用户的身份。

JWT的基本原理,基本流程如下:

  1. 客户端使用账号和密码请求登录接口。

  2. 登录成功后服务器使用签名密钥生成JWT,然后返回JWT给客户端。

  3. 客户端再次向服务端请求其他接口时会带上JWT。

  4. 服务器接收到JWT后验证签名的有效性,对客户端做出相应的响应。

    但是如果说使用多个服务器时候,网上说这好像就会出问题。在单点登录的时候如果使用cookie或写入redis持久化session数据,但是万一持久化又出问题了呢,或者系统出问题了咋办,而且感觉改个redis啥的好懒得搞会改逻辑层好多代码,因此我这里我学习了jwt。

先了解下jwt的功能
https://blog.csdn.net/weixin_42873937/article/details/82460997
下面是jwt的小demo实现
先建一个springboot的项目引入包以及jisontoken包

<dependency>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- mysql连接的jar包 -->

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
        

修改配置文件

server.port=8089
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url= 
spring.datasource.username=
spring.datasource.password=

controller层,根据用户名查找登录信息是否对,如果对生成token,并记录下登录时间,方便在其第二第登录操作时候通过比较时间看token是否失效,我在这里没有验证

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserRepository userRepository;



    //注册或登录
    @RequestMapping("/login")
    @Transactional
    public UserResponse login(User user){

        String username = user.getUsername();
        String password = user.getPassword();
        //TODO  检验参数的完整性

        UserResponse userResponse = new UserResponse();
        User tUser = userRepository.findByUsername(username);
        //检验username是否存在
        user.setLastLoginTime(new Date());
        if(tUser!=null){
            //检验密码是否正确
            if(!tUser.getPassword().equals(password)) {
                userResponse.setErrorNum(Constants.ERR_NUM_PWD_ERR);
                userResponse.setErrorMsg(Constants.ERR_MSG_PWD_ERR);
                return userResponse;
            }
            userRepository.updateLastLoginTimeByUserName(user.getLastLoginTime(),username);

        }else {
            try {
                tUser = userRepository.save(user);
            } catch (Exception e) {
                userResponse.setErrorNum(Constants.ERR_NUM_SERVER_ERR);
                userResponse.setErrorMsg(Constants.ERR_MSG_SERVER_ERR);
                return userResponse;
            }
        }
        userResponse.setErrorNum(Constants.ERR_NUM_OK);
        userResponse.setErrorMsg(Constants.ERR_MSG_OK);
        userResponse.setUserName(username);
        userResponse.setUserId(tUser.getId());
        userResponse.setToken(JwtUtil.generateToken(username,user.getLastLoginTime()));

        return userResponse;
    }

其他登录:

package com.yccj.controller;

import com.yccj.util.JwtUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
*功能描述
*@author hua_wen
*@date 下午 08:10 2019/12/28
*@param 
*@return  
*/
@RestController

public class HelloController {
    @RequestMapping("/otherLongin")
    public Map login(HttpServletRequest request){
        String token = request.getParameter("token");
        return  JwtUtil.validateToken(token);
    }
}

实体类

package com.yccj.entity;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import java.util.Date;

/**
*功能描述
*@author hua_wen
*@date 下午 07:40 2019/12/28
*@param 
*@return  
*/
@Data
@Entity
public class User {
    @Id
    @GeneratedValue(strategy= GenerationType.AUTO)//用于标注主键的生成策略,通过strategy 属性指定。默认情况下,JPA 自动选择一个最适合底层数据库的主键生成策略
    private Integer id;

    private String username;

    private String password;

    private Date lastLoginTime;


}

jwt工具类

package com.yccj.util;

import com.yccj.repository.UserRepository;


import io.jsonwebtoken.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
*功能描述
*@author hua_wen
*@date 下午 07:28 2019/12/28
*@param
*@return
*/
@Component
public class JwtUtil {

    private static UserRepository userRepository;

    @Autowired
    public JwtUtil(UserRepository userRepository) {
        JwtUtil.userRepository = userRepository;
    }




    public static final long EXPIRATION_TIME = 3600_000_000L; // 1000 hour
    static final String SECRET = "ThisIsASecret";
    static final String TOKEN_PREFIX = "Bearer";
    static final String HEADER_STRING = "Authorization";

    public static String generateToken(String username,Date generateTime) {
        HashMap<String, Object> map = new HashMap<>();
        //you can put any data in the map

        map.put("username", username);
        map.put("generateTime",generateTime);
        String jwt = Jwts.builder()
                .setClaims(map)
                .setExpiration(new Date(generateTime.getTime() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
        return jwt;
    }

    /**
     *
     * @param token
     * @return
     */
    public static Map<String,Object> validateToken(String token) {
        Map<String,Object> resp = new HashMap<String,Object>();
        if (token != null) {
            // parse the token.

            try {
                Map<String, Object> body = Jwts.parser()
                        .setSigningKey(SECRET)
                        .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                        .getBody();
                String username = (String) (body.get("username"));
                Date generateTime = new Date((Long)body.get("generateTime"));



                if(username == null || username.isEmpty()){
                    resp.put("ERR_MSG",Constants.ERR_MSG_USERNAME_EMPTY);
                    return resp;
                }
                //账号在别处登录
                if(userRepository.findByUsername(username).getLastLoginTime().after(generateTime)){
                    resp.put("ERR_MSG",Constants.ERR_MSG_LOGIN_DOU);
                    return resp;
                }

                resp.put("username",username);
                resp.put("generateTime",generateTime);

                return resp;
            }catch (SignatureException | MalformedJwtException e) {
                // TODO: handle exception
                // don't trust the JWT!
                // jwt 解析错误
                resp.put("ERR_MSG",Constants.ERR_MSG_TOKEN_ERR);
                return resp;

            } catch (ExpiredJwtException e) {
                // jwt 已经过期,在设置jwt的时候如果设置了过期时间,这里会自动判断jwt是否已经过期,如果过期则会抛出这个异常,我们可以抓住这个异常并作相关处理。
                resp.put("ERR_MSG",Constants.ERR_MSG_TOKEN_EXP);
                return resp;
            }

        }else {
            resp.put("ERR_MSG",Constants.ERR_MSG_TOKEN_EMPTY);
            return resp;
        }



    }


}

网页错误信息

package com.yccj.util;
/**
*功能描述
*@author hua_wen
*@date 下午 07:28 2019/12/28
*@param
*@return
*/
public class Constants {

    public static final int ERR_NUM_OK = 200;
    public static final String ERR_MSG_OK = "成功";

    public static final int ERR_NUM_USER_EXIST = 410;
    public static final String ERR_MSG_USER_EXIST = "用户已存在";
    public static final int ERR_NUM_PWD_ERR = 411;
    public static final String ERR_MSG_PWD_ERR = "密码错误";
    public static final int ERR_NUM_USERNAME_EMPTY = 412;
    public static final String ERR_MSG_USERNAME_EMPTY = "用户名不能为空";
    public static final int ERR_NUM_TOKEN_ERR = 413;
    public static final String ERR_MSG_TOKEN_ERR = "Token解析错误";
    public static final int ERR_NUM_TOKEN_EXP = 414;
    public static final String ERR_MSG_TOKEN_EXP = "Token已过期";
    public static final int ERR_NUM_TOKEN_EMPTY = 415;
    public static final String ERR_MSG_TOKEN_EMPTY = "Token不能为空";
    public static final int ERR_NUM_LOGIN_DOU = 416;
    public static final String ERR_MSG_LOGIN_DOU = "已在别处登录";

    public static final int ERR_NUM_SERVER_ERR = 500;
    public static final String ERR_MSG_SERVER_ERR = "服务器错误";
}

查询类

package com.yccj.repository;

import com.yccj.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Date;

/**
*功能描述
*@author hua_wen
*@date 下午 07:49 2019/12/28
*@param 
*@return  
*/
public interface UserRepository extends JpaRepository<User, Long> {

    User findByUsername(String name);
    //@Query 与 @Modifying这两个annotation一起声明,可定义个性化更新操作
    @Modifying
    @Query("update User set lastLoginTime = :lastLoginTime where username =:username")
    void updateLastLoginTimeByUserName(@Param("lastLoginTime")Date lastLoginTime, @Param("username")String username);

}

页面返回

package com.yccj.Response;

import lombok.Data;


/**
*功能描述
*@author hua_wen
*@date 下午 07:48 2019/12/28
*@param
*@return
*/
@Data
public class UserResponse extends BaseResponse{
    private String userName;
    private Integer userId;
    private String token;
}

package com.yccj.Response;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;

/**
*功能描述
*@author hua_wen
*@date 下午 07:48 2019/12/28
*@param
*@return
*/
@Data
public class BaseResponse {
private int errorNum;
private String errorMsg;
}
完成后访问界面并传入登录信息username和password
在这里插入图片描述
进行其他登录的时候会将刚刚返回页面的token请求后台可以得到在这里插入图片描述
在此时如果前台页面不携带token会玩来给后台,那么后台就可以进行判断,然后进行权限拦截等等操作
在这里插入图片描述
在后台传回来的token,网页也会保存在cokie中,在前端可以获得token并每次携带会服务端
在这里插入图片描述

学如逆水行舟,不进则退

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

健身小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值