银行模拟系统2.0 完整代码见地址:https://github.com/yeyuting-1314/tokenLogin-2.0.git

一、准备工作

我们要将redis进行对象缓存,我们就要实现redis缓存数据的序列化和反序列化,所谓序列化就是我们的value对象拿到对应内容存储到redis中的途中要将对象内容转化为字节流,传输到redis中,接着redis中要存储下来的时候需要将字节流转化为对象内容存储下来,便于我们进行存取查询操作,这里就需要实现redis 的反序列化了,首先我们就对redis进行相关的配置。

1. redis序列化,返回实体对象实现序列化接口,同时写入对应的序列编号(有时候也可以不加,但是有些时候不加的化会出现序列化编号的序列化对应不上的情况,进而出错,所以最好还是将编号也加上)这个位置不加容易出现异常,想了解可以看文章:

public class User implements Serializable {

    private static final long serialVersionUID = 3529219554011221820L;
//其他代码

}

2. redis反序列化key和value对象,新建一个RedisConfig类,类中对反序列进行配置

/**
 * @author yeyuting
 * @create 2021/2/19
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        template.setConnectionFactory(factory);
        //key序列化方式
        template.setKeySerializer(redisSerializer);
        //value序列化
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        //value hashmap序列化
        template.setHashValueSerializer(redisSerializer);
        return template;
        }
    }

这里对反序列化进行较多研究,想深入了解可以阅读文章:

3. application.properties中对redis进行接口配置

#redis配置
spring.redis.port=6379
spring.redis.host=localhost

4. token的生成和解密重新设置生成方法。为啥之前的token生成方法不可以用了呢,这是因为我们需要一套token生成和解密方法,可以顺利实现token生成和解密,因此这里就给出了一套生成token和解密token的方法,便于后面将token进行解密进而拿到生成token的元素,例如用户名字等等。如下:

public String getToken(User user) {
        String token = null;
        try {
            Date expiresAt = new Date(System.currentTimeMillis() + 24L * 60L * 3600L * 1000L);
            token = JWT.create()
                    .withIssuer("auth0")
                    .withClaim("username", user.getUserName())
                    .withClaim("password", user.getPassword())
                    .withClaim("account" , user.getAccount())
                    .withExpiresAt(expiresAt)
                    // 使用了HMAC256加密算法。
                    // mysecret是用来加密数字签名的密钥。
                    .sign(Algorithm.HMAC256("mysecret"));
        } catch (JWTCreationException exception){
            //Invalid Signing configuration / Couldn't convert Claims.
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return token;
    }

    public DecodedJWT deToken(final String token) {
        DecodedJWT jwt = null;
        try {
            // 使用了HMAC256加密算法。
            // mysecret是用来加密数字签名的密钥。
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256("mysecret"))
                    .withIssuer("auth0")
                    .build(); //Reusable verifier instance
            jwt = verifier.verify(token);
        } catch (JWTVerificationException exception) {
            //Invalid signature/claims
            exception.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return jwt;
    }

 这样一来,准备工作就做好了,加下来集成redis存储对象。

二、正式工作

1. 首先用户登陆,访问loginCheck接口,如果数据库有该用户信息,则生成新的token并从数据库中拿到用户信息,以一个user对象作为value,用户名作为key存储到redis中,代码修改如下:

public Result loginCheck(User user , HttpServletResponse response){
        User user1 = userMapper.selectByName(user.getUserName()) ;
        if(user1 == null ){
            return Results.failure("用户不存在") ;
        }else if(!user1.getPassword().equals(user.getPassword())){
            return Results.failure("密码输入错误!") ;
        }
        ValueOperations valueOperations = redisTemplate.opsForValue() ;
        User newUser = (User)valueOperations.get(user1.getUserName()) ;

        if(newUser != null){
            jedisUtil.delString(user1.getUserName());
        }
        String token = tokenUtil.getToken(user1) ;
        System.out.println("token:" + token);
        user1.setToken(token);
        valueOperations.set(user1.getUserName() , user1);

        //jedisUtil.tokenToJedis(user1);
        return Results.successWithData(user1);
    }

postman访问情况后结果如下:

真实银行业务查询余额模块的JAVA开发流程 银行余款查询_redis

 

 这时redis中成功存储了用户基本信息。

真实银行业务查询余额模块的JAVA开发流程 银行余款查询_序列化_02

 

 2.其他接口访问时都要先进入拦截器进行token认证,要记得将上面的token塞进各个接口头信息中,在这里我们就能看到token解密的魅力了,进行token认证的时候我们从头信息中只能拿到token信息,其余信息都拿不到,这时我们需要将此token和redis中的token对比,看是否能对应的上,这时token解密出来的用户名就派上了重要用场了。

/**
 * @author yeyuting
 * @create 2021/1/26
 */
public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    TokenUtil tokenUtil ;

    @Autowired
    RedisTemplate redisTemplate ;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        String token = request.getHeader("token") ;
        //Jedis jedis = new Jedis("localhost" , 6379) ;
        if(StringUtils.isEmpty(token)){
            response.sendRedirect("/sys/user/login");
            return false ;
        }
        DecodedJWT jwt = tokenUtil.deToken(token) ;
        String userName = jwt.getClaim("username").asString() ;

        if((userName == null) ||(userName.trim().equals("")) ){
            response.sendRedirect("/sys/user/login");
            return false ;
        }
        System.out.println("token匹配成功!");
        return true ;
    }



}

(1)token认证通过后,紧接进行转账接口访问,记得要将上面的token塞进转账接口头信息中。

//转账
    public String transferAccount(Double accountMoney , String targetAccount , HttpServletRequest request){

        String token = request.getHeader("token") ;
        DecodedJWT jwt = tokenUtil.deToken(token) ;
        Double account = jwt.getClaim("account").asDouble() ;
        String userName = jwt.getClaim("username").asString() ;

        if(accountMoney > account){
            return "余额不足" ;
        }
        User user1 = userMapper.selectByName(targetAccount) ;
        if (user1.equals(null)){
            return "对方账户不存在" ;
        }
        //转出账户余额更新
        boolean result = userMapper.updateAccountOut(accountMoney , userName) ;
        //转入账户余额更新
        boolean result1 = userMapper.updateAccountIn(accountMoney , user1.getUserName()) ;
        if ((result == false)||(result1 == false) ){
            return "转账操作失败" ;
        }
        //转账记录生成------------
        //String accountType = TransactionType.WITHDRAWMONEY ;
        //出账记录生成
        boolean insertReult = userMapper.accountOutInsert(userName ,account ,  accountMoney , targetAccount , TransactionType.WITHDRAWMONEY ) ;
        //入账记录生成
        //String accountType1 = TransactionType.SAVEMONEY ;
        boolean insertReult1 = userMapper.accountInInsert(user1.getUserName() , user1.getAccount() , accountMoney , userName , TransactionType.SAVEMONEY ) ;

        if((insertReult == false) || (insertReult1 == false)){
            return "转账记录生成失败" ;
        }

        return "转账成功!" ;
    }

(2)接下来是入账接口的实现进行修改:

//存钱
    public String saveMoney(Double accountMoney , HttpServletRequest request){
        String token = request.getHeader("token") ;
        DecodedJWT jwt = tokenUtil.deToken(token) ;
        Double account = jwt.getClaim("account").asDouble() ;
        String userName = jwt.getClaim("username").asString() ;
        //存入余额更新
        boolean result = userMapper.updateAccountIn(accountMoney , userName) ;
        if(result = false){
            return "存入失败" ;
        }
        //存入记录生成
        boolean insertResult = userMapper.accountInInsert(userName ,account , accountMoney , userName , TransactionType.SAVEMONEY) ;
        if((insertResult == false)){
            return "入账记录生成失败" ;
        }
        return "成功存入" + accountMoney + "元!"  ;
    }

(3)然后是出账接口实现修改:

//取钱
    public String withdrawMoney(Double accountMoney , HttpServletRequest request){
        String token = request.getHeader("token") ;
        DecodedJWT jwt = tokenUtil.deToken(token) ;
        Double account = jwt.getClaim("account").asDouble() ;
        String userName = jwt.getClaim("username").asString() ;

        if(accountMoney > account){
            return "余额不足" ;
        }
        boolean result = userMapper.updateAccountOut(accountMoney , userName) ;
        if(result = false){
            return "取钱失败" ;
        }
        //出账记录生成
        boolean insertResult = userMapper.accountOutInsert(userName ,account, accountMoney , userName , TransactionType.WITHDRAWMONEY) ;
        if((insertResult == false)){
            return "出账记录生成失败" ;
        }
        return "成功取出" + accountMoney + "元!"  ;
    }

(4)最后是查询接口的实现修改:

//查询余额
    public Double selectByUserName1(String username , HttpServletRequest request) {
        String token = request.getHeader("token") ;
        DecodedJWT jwt = tokenUtil.deToken(token) ;
        Double account = jwt.getClaim("account").asDouble() ;
        return account ;
    }

这样一来,redis集成模拟银行转账、入账、出账、查询余额也就实现了。

至此,结束。