博客项目(6、登录接口)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 博客项目(6、登录接口)

一、前言

在上一章中我对个人博客项目进行了全局统一处理,包括全局异常处理,统一全局响应参数,使用mybatis-plus的分页插件等

本章主要完成的是登录接口,通过登录接口来整合redis和jwt,同时为创建UtilController,将一些公共接口放置在UtilController中

二、整合redis

导入redis依赖

<!--        Redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>${jedis.version}</version>
</dependency>
复制代码

redisUtils类

在实际工作中,我们不会使用RedisTemplate来进行redis的操作,因为太繁琐了,所以这里写了一个RedisUtils工具类

package com.ningxuan.blog.common.redis;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public final class RedisUtils {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    public Set<String> keys(String keys){
        try {
            return redisTemplate.keys(keys);
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 指定缓存失效时间
     * @param key 键
     * @param time 时间(秒)
     * @return
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
    /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
            }
        }
    }
    /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }
    /**
     * 普通缓存放入
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 普通缓存放入, 不存在放入,存在返回
     * @param key 键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean setnx(String key, Object value) {
        try {
            redisTemplate.opsForValue().setIfAbsent(key,value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 普通缓存放入并设置时间
     * @param key 键
     * @param value 值
     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 普通缓存放入并设置时间,不存在放入,存在返回
     * @param key 键
     * @param value 值
     * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean setnx(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 递增
     * @param key 键
     * @param delta 要增加几(大于0)
     * @return
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }
    /**
     * 递减
     * @param key 键
     * @param delta 要减少几(小于0)
     * @return
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }
    /**
     * HashGet
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @return 值
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }
    /**
     * 获取hashKey对应的所有键值
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * HashSet
     * @param key 键
     * @param map 对应多个键值
     * @return true 成功 false 失败
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * HashSet 并设置时间
     * @param key 键
     * @param map 对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 向一张hash表中放入数据,如果不存在将创建
     * @param key 键
     * @param item 项
     * @param value 值
     * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 删除hash表中的值
     * @param key 键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }
    /**
     * 判断hash表中是否有该项的值
     * @param key 键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }
    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     * @param key 键
     * @param item 项
     * @param by 要增加几(大于0)
     * @return
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }
    /**
     * hash递减
     * @param key 键
     * @param item 项
     * @param by 要减少记(小于0)
     * @return
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }
    /**
     * 根据key获取Set中的所有值
     * @param key 键
     * @return
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 根据value从一个set中查询,是否存在
     * @param key 键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 将数据放入set缓存
     * @param key 键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    /**
     * 将set数据放入缓存
     * @param key 键
     * @param time 时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0){
                expire(key, time);
            }
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    /**
     * 获取set缓存的长度
     * @param key 键
     * @return
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    /**
     * 移除值为value的
     * @param key 键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    // ===============================list=================================
    /**
     * 获取list缓存的内容
     * @param key 键
     * @param start 开始
     * @param end 结束 0 到 -1代表所有值
     * @return
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 获取list缓存的长度
     * @param key 键
     * @return
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
    /**
     * 通过索引 获取list中的值
     * @param key 键
     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
     * @return
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0){
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 将list放入缓存
     * @param key 键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 将list放入缓存
     *
     * @param key 键
     * @param value 值
     * @param time 时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0){
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 根据索引修改list中的某条数据
     * @param key 键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 移除N个值为value
     * @param key 键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lRemove(String key, long count, Object value) {
        try {
            Long remove = redisTemplate.opsForList().remove(key, count, value);
            return remove;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
}
复制代码

redis配置

@Configuration
public class RedisConfig {
    @Resource
    private RedisConnectionFactory factory;
    @Bean
    public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer(){
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        return jackson2JsonRedisSerializer;
    }
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }
}
复制代码

redisKey统一管理类

项目开发中,可能会有很多种情况要对数据进行redis的存储,为了方便统一管理这里建立了一个RedisKeys类

package com.ningxuan.blog.common.redis;
public class RedisKeys {
    /**
     * 验证码Key
     */
    public static String getCaptchaKey(String uuid){
        return "sys:captcha:" + uuid;
    }
    /**
     * 登录用户Key
     */
    public static String getSecurityUserKey(Long id){
        return "sys:security:user:" + id;
    }
}
复制代码

由于我们的时间格式都是LocalDateTime的,存入redis的时候不能序列化和反序列化,所以我们要新增依赖

<!--        json转换-->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.13.0</version>
</dependency>
复制代码

并且在代码中使用相应的注解@JsonDeserialize和@JsonSerialize

@ApiModelProperty("创建时间")
@TableField("create_time")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" , timezone = "GMT+8")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)     // 反序列化
@JsonSerialize(using = LocalDateTimeSerializer.class)     // 序列化
private LocalDateTime createTime;
复制代码

三、整合jwt

其实不整个jwt也无所谓, 只单纯使用token生成工具就可以,像我一样是单纯为了使用而使用其实挺没必要的

因为暂时仅仅当成一个token生成工具,所以过期时间之类的都没有做处理

/**
     * 生成JWT TOKEN
     *
     * @param id 这里加密数据id为用户id
     * @return
     */
    public String generateToken(Long id) {
        /**将token设置为jwt格式*/
        String baseToken = UUID.randomUUID().toString();
        Claims jwtClaims = Jwts.claims().setSubject(baseToken);
        jwtClaims.put("jwt", id);
        String compactJws = Jwts.builder()
                .setClaims(jwtClaims)
                .signWith(SignatureAlgorithm.HS256, "jwt")
                .compact();
        return compactJws;
    }
复制代码

四、新建UtilController

这边返回的实际上应该是SysUser用户信息,目前做的有问题,后面会改一下

@RestController
@RequestMapping("/utils")
public class UtilsController {
    @Resource
    private ISysUserService userService;
    @PostMapping("login")
    @ApiOperation("登录接口")
    public ResultVo login(@RequestBody LoginDto loginDto, HttpServletResponse response){
        String token = userService.login(loginDto);
        System.out.println(token);
        if (token != null){
            // token不为null存入cookie
            CookieUtils.setUpCookie(response, CommonConstant.COOKIE_TOKEN_KEY, token);
            return new ResultVo();
        }
        return new ResultVo().error();
    }
}
复制代码

五、用户模块接口开发

主要完成了一个详情功能,而且还是写死的。。。

@RestController
@RequestMapping("/user")
public class SysUserController {
    @Resource
    private ISysUserService sysUserService;
    @PostMapping("list")
    @ApiOperation("查看列表")
    public ResultVo getList(PageVo page){
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(page.getKeyword() != null, SysUser::getNickname, page.getKeyword());
        List<SysUser> list = sysUserService.list(wrapper);
        return new ResultVo(list);
    }
    @GetMapping
    @ApiOperation("查看详情")
    public ResultVo getInfo(){
        return new ResultVo();
    }
    @PostMapping
    @ApiOperation("新增")
    public ResultVo insert(){
        SysUser sysUser = new SysUser();
        sysUser.setUsername("ningxuan");
        sysUser.setPassword("123456");
        sysUserService.save(sysUser);
        return new ResultVo();
    }
    @PutMapping
    @ApiOperation("修改")
    public ResultVo update(){
        return new ResultVo();
    }
    @DeleteMapping
    @ApiOperation("删除")
    public ResultVo delete(){
        return new ResultVo();
    }
}
复制代码

编写统一接口,增删改查,并为每一个Controller进行CV编程

@PostMapping("list")
@ApiOperation("查看列表")
public ResultVo getList(PageVo page){
    return new ResultVo();
}
@GetMapping
@ApiOperation("查看详情")
public ResultVo getInfo(){
    return new ResultVo();
}
@PostMapping
@ApiOperation("新增")
public ResultVo insert(){
    return new ResultVo();
}
@PutMapping
@ApiOperation("修改")
public ResultVo update(){
    return new ResultVo();
}
@DeleteMapping
@ApiOperation("删除")
public ResultVo delete(){
    return new ResultVo();
}
复制代码

六、login Service详情

@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements ISysUserService {
    @Resource
    private RedisUtils redisUtils;
    @Override
    public String login(LoginDto loginDto) {
        LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(SysUser::getUsername, loginDto.getUsername());
        SysUser one = this.getOne(wrapper);
        if (one == null){
            //TODO 自定义异常
            log.error("用户不存在");
            return null;
        }
        boolean equals = one.getPassword().equals(loginDto.getPassword());
        if (!equals){
            //TODO 自定义异常
            log.error("账号或密码错误");
            return null;
        }
        //TODO 密码错误之后生成token判断错误次数,超过五次锁用户
        // 使用jwt生成token
        String token = JwtUtils.generateToken(one.getBlid());
        // 使用redis保存token   默认8小时
        redisUtils.set(RedisKeys.getLoginKey(token), one, CommonConstant.REDIS_TIME_TOKEN);
        return token;
    }
}
复制代码

七、测试

尝试登录, token正常生成

网络异常,图片无法展示
|

网络异常,图片无法展示
|

八、总结

中间有很多遗漏,只能后续慢慢完成了,距离自己的想法还有段距离


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
3天前
|
JavaScript 前端开发 容器
如何使用Contentlayer和Tocbot创建博客网站目录?
如何使用Contentlayer和Tocbot创建博客网站目录?
12 0
|
5月前
|
数据库
【博客项目】—登录功能实现( 四)
【博客项目】—登录功能实现( 四)
|
5月前
【博客项目】—登录验证功能实现( 五)
【博客项目】—登录验证功能实现( 五)
|
5月前
|
数据库
【博客项目】—用户新增功能(九)
【博客项目】—用户新增功能(九)
|
5月前
【博客项目】—用户修改功能(十一)
【博客项目】—用户修改功能(十一)
|
5月前
【博客项目】—用户删除功能(十二)
【博客项目】—用户删除功能(十二)
|
前端开发 小程序 BI
【博客开发】开发进度:基本界面已实现
【博客开发】开发进度:基本界面已实现
74 0
【博客开发】开发进度:基本界面已实现
如何实现 请在微信客户端打开链接
如何实现 请在微信客户端打开链接
172 0
如何实现 请在微信客户端打开链接
|
数据库 数据安全/隐私保护
5、后台基础权限框架搭建实现[木字楠博客]
5、后台基础权限框架搭建实现[木字楠博客]
76 0
5、后台基础权限框架搭建实现[木字楠博客]
博客园社区登录帐号和第三方绑定的设置
博客园社区登录帐号和第三方绑定的设置
博客园社区登录帐号和第三方绑定的设置