关于核心登录逻辑请对照上篇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;
}