spring-security+jwt+redis实现登录

关于核心登录逻辑请对照上篇spring-security+jwt实战,下面主要介绍验证码接口实现。

核心api

package com.zhouy.modules.security.rest;

import cn.hutool.core.util.IdUtil;
import com.wf.captcha.*;
import com.zhouy.annotation.AnonymousAccess;
import com.zhouy.common.utils.EncryptUtils;
import com.zhouy.exception.BadRequestException;
import com.zhouy.modules.monitor.service.RedisService;
import com.zhouy.modules.security.security.AuthInfo;
import com.zhouy.modules.security.security.AuthUser;
import com.zhouy.modules.security.security.ImgResult;
import com.zhouy.modules.security.security.JwtUser;
import com.zhouy.modules.security.service.OnlineUserService;
import com.zhouy.modules.security.utils.JwtTokenUtil;
import com.zhouy.modules.system.domain.User;
import com.zhouy.modules.system.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

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

@Slf4j
@RestController
@RequestMapping("/auth")
public class AuthenticationController {
    private final RedisService redisService;
    private final JwtTokenUtil jwtTokenUtil;
    private final OnlineUserService onlineUserService;
    private final UserDetailsService userDetailsService;
    public AuthenticationController (RedisService redisService, @Qualifier("jwtUserDetailsService") UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil, OnlineUserService onlineUserService){
        this.redisService = redisService;
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.onlineUserService = onlineUserService;
    }
    @AnonymousAccess
    @GetMapping(value = "/captcha")
    public ImgResult getCaptcha(){
        //png类型
        //SpecCaptcha captcha = new SpecCaptcha(111,36);
        //gif类型
        //GifCaptcha captcha = new GifCaptcha(111,36);
        //算数类型
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111,36);
        captcha.setLen(2);
        //中文类型
        //ChineseCaptcha captcha = new ChineseCaptcha(111,36);
        //中文gif类型
        //ChineseGifCaptcha captcha = new ChineseGifCaptcha(111,36);
        String result = captcha.text();//结果
        String uuid = IdUtil.simpleUUID();
        redisService.set(uuid,result);
        return new ImgResult(captcha.toBase64(),uuid);
    }
    @AnonymousAccess
    @PostMapping(value = "/login")
    public ResponseEntity login(@Validated @RequestBody AuthUser authUser, HttpServletRequest request){
        //1.查询验证码
        String captcha = redisService.get(authUser.getUuid());
        //2.删除验证码缓存
        redisService.delete(authUser.getUuid());
        //3.验证
        if (StringUtils.isEmpty(captcha)) throw new BadRequestException("验证码已过期");
        if (StringUtils.isEmpty(authUser.getCaptcha()) || !authUser.getCaptcha().equalsIgnoreCase(captcha)) throw new BadRequestException("验证码错误");
        JwtUser jwtUser = (JwtUser) userDetailsService.loadUserByUsername(authUser.getUsername());
        if (!jwtUser.getPassword().equals(EncryptUtils.encryptPassword(authUser.getPassword()))) throw new BadRequestException("密码错误");
        if (!jwtUser.isEnabled()) throw new BadRequestException("账号已停用,请联系管理员");
        //4.生成令牌
        final String token = jwtTokenUtil.generateToken(jwtUser);
        //5.新增在线用户
        onlineUserService.save(jwtUser,token,request);
        return ResponseEntity.ok(new AuthInfo(token,jwtUser));
    }

    @AnonymousAccess
    @DeleteMapping(value = "/logout")
    public ResponseEntity logout(HttpServletRequest request){
        onlineUserService.logout(jwtTokenUtil.getToken(request));
        return new ResponseEntity(HttpStatus.OK);
    }
}

生成验证码并存入redis中,代码分解

@AnonymousAccess
    @GetMapping(value = "/captcha")
    public ImgResult getCaptcha(){
        //png类型
        //SpecCaptcha captcha = new SpecCaptcha(111,36);
        //gif类型
        //GifCaptcha captcha = new GifCaptcha(111,36);
        //算数类型
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111,36);
        captcha.setLen(2);
        //中文类型
        //ChineseCaptcha captcha = new ChineseCaptcha(111,36);
        //中文gif类型
        //ChineseGifCaptcha captcha = new ChineseGifCaptcha(111,36);
        String result = captcha.text();//结果
        String uuid = IdUtil.simpleUUID();//hutool生成uuid
        redisService.set(uuid,result);
        return new ImgResult(captcha.toBase64(),uuid);
    }

1.pom.xml引入验证码工具、redis以及spring cache

        
        
        
            com.github.whvcse
            easy-captcha
            1.6.2
        
        
        
            org.springframework.boot
            spring-boot-starter-cache
        
        
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
        
        
            org.apache.commons
            commons-pool2
            2.6.0
           
        
        
        
            cn.hutool
            hutool-all
            ${hutool.version}
             

2.redis接口以及实现类

package com.zhouy.modules.monitor.service;

import com.zhouy.modules.monitor.domain.vo.RedisVo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.TimeUnit;

public interface RedisService {
    void expire(String key,Long expiration,TimeUnit time);
    /**
     * @param key
     * @param pageable
     * @return
     */
    Page findByKey(String key, Pageable pageable);

    List findByKey(String key);

    /**
     * @param key
     * @return
     */
    String get(String key);

    /**
     * @param key 键
     * @param val 值
     */
    void set(String key, Object val);

    /**
     * 删除
     * @param key
     */
    void delete(String key);

    /**
     * 清空缓存
     */
    void deleteAll();
package com.zhouy.modules.monitor.service.impl;
import com.zhouy.common.utils.FileUtil;
import com.zhouy.common.utils.PageUtil;
import com.zhouy.modules.monitor.domain.vo.RedisVo;
import com.zhouy.modules.monitor.service.RedisService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * redis指令参考:https://www.redis.net.cn/order/
 */
@Service
public class RedisServiceImpl implements RedisService {

    private final RedisTemplate redisTemplate; //redis模板

    public RedisServiceImpl(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; }

    @Value("${loginCaptcha.expiration}")
    private Long expiration;

    @Override
    public void expire(String key, Long expiration, TimeUnit time) {
        redisTemplate.expire(key,expiration,time);
    }

    @Override
    public Page findByKey(String key, Pageable pageable) {
        List redisVos = findByKey(key);
        return new PageImpl(
                PageUtil.toPage(pageable.getPageNumber(),pageable.getPageSize(),redisVos),
                pageable,
                redisVos.size());
    }

    @Override
    public List findByKey(String key) {
        List redisVos = new ArrayList<>();
        //KEYS指令:获取所有key,查询方式类似sql的like,*与%等价
        if (!"*".equals(key)){
            key = "*"+key+"*";
        }
        Set keys = redisTemplate.keys(key);
        for(String s : keys){
           RedisVo redisVo = new RedisVo(s, Objects.requireNonNull(redisTemplate.opsForValue().get(s)).toString());
           redisVos.add(redisVo);
        }
        return redisVos;
    }

    @Override
    public String get(String key) {
        try{
            return Objects.requireNonNull(redisTemplate.opsForValue().get(key)).toString();
        }catch (Exception e){
            return "";
        }
    }

    @Override
    public void set(String key, Object val) {
        redisTemplate.opsForValue().set(key,val);
        redisTemplate.expire(key,expiration, TimeUnit.MINUTES);
    }

    @Override
    public void delete(String key) {
        redisTemplate.delete(key);
    }

    @Override
    public void deleteAll() {
        Set keys = redisTemplate.keys("*");
//        redisTemplate.delete(keys.stream().filter(s->!s.contains("onlineKey")).filter(s->!s.contains("codeKey")).collect(Collectors.toList()));
        redisTemplate.delete(keys.stream().collect(Collectors.toList()));
    }
}

3.ImgResult

package com.zhouy.modules.security.security;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data //lombok注解,getter和setter
@AllArgsConstructor //lombok注解,全参构造器
public class ImgResult {
    private String img; //base64
    private String uuid;
}

你可能感兴趣的:(spring-security+jwt+redis实现登录)