springboot +redis+mysql实现简单登录功能
一、创建数据库并连接好
-
创建表名为user
-
引入mysql需要的依赖
<!--mybatis_plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter
</artifactId>
<version>3.5.3.1</version>
</dependency>
</dependency>
<!--mysql驱动依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java
</artifactId>
</dependency>
- 连接好数据库(这里我使用.yml配置的)
spring:
#数据库连接
datasource:
url: jdbc:mysql://localhost:3306/XXXXX?characterEncoding=utf-8&serverTimezone=GMT%2B8
username: XXXX
password: XXXXXX
driver-class-name: com.mysql.cj.jdbc.Driver
- 接着使用easycode这个插件快速生成controller、dao、entity、service、service.impl、mapper
二、配置RedisConfig(目的存放token等)
- 引入redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis
</artifactId>
</dependency>
-
配置redis(.ym文件)
spring data: redis: host: localhost port: 6379 password: xxxxxx #连接超时时间 #连接池 pool: #最大连接数 max-active: 200 #最大等待时间 max-wait: -1 #最大空闲连接数 max-idle: 10 #最小空闲连接数 min-idle: 0 timeout: 2000 database: 1
- redisconfig序列化为了存进去的是JSON格式
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)throws UnknownHostException {
RedisTemplate<String,Object> template=new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//Json序列化配置指的是在将Java对象转换为Json字符串时
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class);
// 创建一个Jackson2JsonRedisSerializer对象,并指定要序列化的Java对象类型为Object.class。这将使得RedisTemplate可以序列化任意类型的Java对象
ObjectMapper objectMapper=new ObjectMapper();
// 创建一个ObjectMapper对象,它是Jackson库进行Json序列化和反序列化的核心组件。
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//设置ObjectMapper的可见性规则,使其能够获取和处理Java对象的所有属性,不论其访问修饰符是什么。
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
//激活默认类型,以便在Json序列化过程中将Java对象的实际类型信息包含在生成的Json字符串中。这样可以在进行反序列化时恢复对象的具体类型。
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//将配置好的ObjectMapper对象应用于Jackson2JsonRedisSerializer,从而确保RedisTemplate在序列化Java对象时使用指定的配置
//string序列化
StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
//key序列化
template.setKeySerializer(stringRedisSerializer);
//hash的key序列方式
template.setHashKeySerializer(stringRedisSerializer);
//value的序列化方式(JSON序列化)
template.setValueSerializer(jackson2JsonRedisSerializer);
//hash的序列化方式(JSON序列化)
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
三、使用Kaptch生成验证码
- 引入kaptcha依赖
<!--验证码-->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
- 配置kaptcha的文件
@Configuration
public class KaptChaConfig {
@Bean
public Producer kaptChaProducer() {
Properties properties = new Properties();
//边框样式
properties.setProperty("kaptcha.border", "yes"); //是否边框
properties.setProperty("kaptcha.border.color", "black"); //边框颜色
//图片大小
properties.setProperty("kaptcha.image.width", "100"); //图片长度
properties.setProperty("kaptcha.image.height", "60"); //图片宽度
//验证码内容
properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"); //验证码内容集合
properties.setProperty("kaptcha.textproducer.char.length", "4"); //验证码长度
properties.setProperty("kaptcha.textproducer.char.space", "2"); //验证码间隔
properties.setProperty("kaptcha.textproducer.font.names", "微软雅黑"); //验证码字体
properties.setProperty("kaptcha.textproducer.font.size", "32"); //验证码字体大小
properties.setProperty("kaptcha.textproducer.font.color", "0,0,0"); //验证码字体颜色
//干扰项
properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise"); //实现类
properties.setProperty("kaptcha.noise.color", "blue"); //干扰颜色
//渲染效果
//水纹 com.google.code.kaptcha.impl.WaterRipple
//鱼眼 com.google.code.kaptcha.impl.FishEyeGimpy
//阴影 com.google.code.kaptcha.impl.ShadowGimpy
properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.ShadowGimpy");
//背景图片渲染器
properties.setProperty("kaptcha.background.impl", "com.google.code.kaptcha.impl.DefaultBackground");
properties.setProperty("kaptcha.background.clear.from", "251,255,242");
properties.setProperty("kaptcha.background.clear.to", "202,235,216");
//文字渲染器
properties.setProperty("kaptcha.word.impl", "com.google.code.kaptcha.text.impl.DefaultWordRenderer");
//使用默认图片实现类
DefaultKaptcha kaptcha = new DefaultKaptcha();
Config config = new Config(properties);
kaptcha.setConfig(config);
return kaptcha;
}
}
- 在controller通过请求获取
@Slf4j
@RestController
@Tag(name = "验证码")
public class KaptChaController {
@Resource
Producer kaptChaProducer;
@GetMapping(value = "/kaptCha",produces = "image/jpeg")
@Operation(summary = "获取验证码")
public void getKaptCha(HttpServletRequest request,HttpServletResponse response, HttpSession session){
//生成验证码
String text =kaptChaProducer.createText();
//生成图片
BufferedImage image=kaptChaProducer.createImage(text);
//将验证码存入session
//session.setAttribute("kaptCha",text);
//将图片输出给浏览器
response.setContentType("image/jpeg");
ServletOutputStream outputStream= null;
try {
outputStream = response.getOutputStream();
ImageIO.write(image,"jpg",outputStream);
log.info("验证码:"+text);
// 刷新输出流
outputStream.flush();
// 关闭输出流
outputStream.close();
} catch (IOException e) {
log.error("验证码获取失败_________验证码获取失败",e.getMessage());
throw new RuntimeException(e);
}
}
}
四、写登录验证功能
1.创建loginController,写controller层(转递给service层)
@Resource
private UserService userService;
@GetMapping("/login")
@Operation(summary = "登录",description = "登录之后返回token")
public Result login(LoginMessage loginMessage, HttpServletRequest request){
//return userService.login(loginMessage.getUsername(),loginMessage.getPassword(),loginMessage.getCode(),request);
return userService.login(loginMessage.getUsername(),loginMessage.getPassword(),request);
}
2.写service层(传递给serviceimpl)
Result login(String username, String password, HttpServletRequest request);
3.写serviceimpl层(传递给dao层,重要重要!)
@Override
public Result login(String username, String password, HttpServletRequest request) {
//判断验证码
//浏览器使用更好
/* String kaptCha = (String) request.getSession().getAttribute("kaptCha");
System.out.println(request.getSession().getAttribute("kaptCha"));
if (kaptCha == null ||!kaptCha.equals(code)) {
log.info("验证码"+kaptCha);
return Result.error("验证码输入错误,请重新输入");
}*/
User user = userDao.userLogin(username, password);
System.out.println(username+password);
if (user == null) {
log.info("用户不存在或密码不正确");
return Result.error("用户不存在或密码不正确");
}
String token = JwtToken.createToken(user.getUsername());
//存入redis中
Map<String,String> tokenMap=new HashMap<>();
tokenMap.put("token",token);
//redisTemplate.opsForValue().set("userToken",tokenMap);
//redisTemplate.opsForHash().putAll("userToken", tokenMap);
redisTemplate.opsForValue().set("userToken",tokenMap.values());
return Result.success("登录成功", token);
}
4.写dao层(传递给mapper.xml,如果不使用.xml在dao层用注解实现)
User userLogin(@Param("username")String username, String password);
5.到mapper.xml的配置
<!-- 用户登录 -->
<select id="userLogin" resultMap="UserMap">
select
*
from user
where username = #{
username}
</select>
五、说明提示
我在entity层加上了LoginMessage和Result
//LoginMessage
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginMessage {
@Schema(description = "用户名",required = true)
private String username;
@Schema(description = "密码",required = true)
private String password;
/*@Schema(description = "验证码",required = true)
private String code;*/
}
//Result
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private long code;
private String message;
private Object obj;
//成功返回结果
public static Result success(String message){
return new Result(200,message,null);
}
//成功返回结果
public static Result success(String message,Object obj){
return new Result(200,message,obj);
}
//失败返回结果
public static Result error(String message){
return new Result(500,message,null);
}
//失败返回结果
public static Result error(String message,Object obj){
return new Result(500,message,obj);
}
//失败返回结果
Result error(String message){
return new Result(500,message,null);
}
//失败返回结果
public static Result error(String message,Object obj){
return new Result(500,message,obj);
}
//失败返回结果
}
}
- 为什么加Result?
是为了登录失败或成功时返回一个处理数据得到我想看到的形式
- 以上登录功能的代码中的验证码被我注释了,想要使用需要验证码解开注释和,传递参数时加上string code (controller、service、serviceimpl、entity)