目录

    • 简介
    • 原理初探
    • 实现思路
    • 登录接口实现
      • 准备工作
      • 编码实现
    • 效果展示

简介

SecuritySpring 家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。

具体介绍和入门看springSecurity入门

原理初探

在实现之前幺要了解一下登录校验的流程以及SpringSecurity的原理以及认证流程

1、登录校验流程

2、 SpringSecurity完整流程
SpringSecurity的原理其实就是一个过滤器链,内部包含了提供各种功能的过滤器。这里我们可以看看入门案例中的过滤器。

图中只展示了核心过滤器,其它的非核心过滤器并没有在图中展示。

UsernamePasswordAuthenticationFilter:负责处理我们在登陆页面填写了用户名密码后的登陆请求。入门案例的认证工作主要有它负责。

ExceptionTranslationFilter:处理过滤器链中抛出的任何AccessDeniedExceptionAuthenticationException

FilterSecurityInterceptor:负责权限校验的过滤器。

我们可以通过Debug查看当前系统中SpringSecurity过滤器链中有哪些过滤器及它们的顺序。

3、认证流程

概念速查:

  • Authentication接口: 它的实现类,表示当前访问系统的用户,封装了用户相关信息。

  • AuthenticationManager接口:定义了认证Authentication的方法

  • UserDetailsService接口:加载用户特定数据的核心接口。里面定义了一个根据用户名查询用户信息的方法。

  • UserDetails接口:提供核心用户信息。通过UserDetailsService根据用户名获取处理的用户信息要封装成UserDetails对象返回。然后将这些信息封装到Authentication对象中。

实现思路

登录

​ ①自定义登录接口

​ 调用ProviderManager的方法进行认证 如果认证通过生成jwt

​ 把用户信息存入redis

​ ②自定义UserDetailsService

​ 在这个实现类中去查询数据库

校验:

​ ①定义Jwt认证过滤器

​ 获取token

​ 解析token获取其中的userid

​ 从redis中获取用户信息

​ 存入SecurityContextHolder

这里我们主要是实现登录接口的功能
核心思想就是就是将认证流程 中的UserDetailsService 重写其loadUserByUsername的方法,使其从数据库中查询数据,然后用自己的类(我这里是UserLogin)实现UserDetails,在loadUserByUsername中返回UserLogin

登录接口实现

准备工作

添加依赖,添加工具类,创建数据库,编写配置文件
1、添加依赖

 <!--redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--fastjson依赖--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.33</version></dependency><!--jwt依赖--><dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.0</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>

2、添加Redis相关配置

import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.serializer.SerializerFeature;import com.fasterxml.jackson.databind.JavaType;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.type.TypeFactory;import org.springframework.data.redis.serializer.RedisSerializer;import org.springframework.data.redis.serializer.SerializationException;import com.alibaba.fastjson.parser.ParserConfig;import org.springframework.util.Assert;import java.nio.charset.Charset;/** * Redis使用FastJson序列化 ** @author sg */public class FastJsonRedisSerializer<T> implements RedisSerializer<T>{public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");private Class<T> clazz;static{ParserConfig.getGlobalInstance().setAutoTypeSupport(true);}public FastJsonRedisSerializer(Class<T> clazz){super();this.clazz = clazz;}@Overridepublic byte[] serialize(T t) throws SerializationException{if (t == null){return new byte[0];}return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);}@Overridepublic T deserialize(byte[] bytes) throws SerializationException{if (bytes == null || bytes.length <= 0){return null;}String str = new String(bytes, DEFAULT_CHARSET);return JSON.parseObject(str, clazz);}protected JavaType getJavaType(Class<" />> clazz){return TypeFactory.defaultInstance().constructType(clazz);}}
import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.StringRedisSerializer;@Configurationpublic class RedisConfig {@Bean@SuppressWarnings(value = { "unchecked", "rawtypes" })public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}}

封装返回的数据

import com.fasterxml.jackson.annotation.JsonInclude;@JsonInclude(JsonInclude.Include.NON_NULL)public class ResponseResult<T> {/** * 状态码 */private Integer code;/** * 提示信息,如果有错误时,前端可以获取该字段进行提示 */private String msg;/** * 查询到的结果数据, */private T data;public ResponseResult(Integer code, String msg) {this.code = code;this.msg = msg;}public ResponseResult(Integer code, T data) {this.code = code;this.data = data;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}public T getData() {return data;}public void setData(T data) {this.data = data;}public ResponseResult(Integer code, String msg, T data) {this.code = code;this.msg = msg;this.data = data;}}

JWT工具类

import io.jsonwebtoken.Claims;import io.jsonwebtoken.JwtBuilder;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.util.Base64;import java.util.Date;import java.util.UUID;/** * JWT工具类 */public class JwtUtil {//有效期为public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000一个小时//设置秘钥明文public static final String JWT_KEY = "sangeng";public static String getUUID(){String token = UUID.randomUUID().toString().replaceAll("-", "");return token;}/** * 生成jtw * @param subject token中要存放的数据(json格式) * @return */public static String createJWT(String subject) {JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间return builder.compact();}/** * 生成jtw * @param subject token中要存放的数据(json格式) * @param ttlMillis token超时时间 * @return */public static String createJWT(String subject, Long ttlMillis) {JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间return builder.compact();}private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;SecretKey secretKey = generalKey();long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);if(ttlMillis==null){ttlMillis=JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);return Jwts.builder().setId(uuid)//唯一的ID.setSubject(subject) // 主题可以是JSON数据.setIssuer("sg") // 签发者.setIssuedAt(now)// 签发时间.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥.setExpiration(expDate);}/** * 创建token * @param id * @param subject * @param ttlMillis * @return */public static String createJWT(String id, String subject, Long ttlMillis) {JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间return builder.compact();}public static void main(String[] args) throws Exception {String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";Claims claims = parseJWT(token);System.out.println(claims);}/** * 生成加密后的秘钥 secretKey * @return */public static SecretKey generalKey() {byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/** * 解析 * * @param jwt * @return * @throws Exception */public static Claims parseJWT(String jwt) throws Exception {SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}}

Redis工具类

import java.util.*;import java.util.concurrent.TimeUnit;@SuppressWarnings(value = { "unchecked", "rawtypes" })@Componentpublic class RedisCache{@Autowiredpublic RedisTemplate redisTemplate;/** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 */public <T> void setCacheObject(final String key, final T value){redisTemplate.opsForValue().set(key, value);}/** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @param timeout 时间 * @param timeUnit 时间颗粒度 */public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit){redisTemplate.opsForValue().set(key, value, timeout, timeUnit);}/** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @return true=设置成功;false=设置失败 */public boolean expire(final String key, final long timeout){return expire(key, timeout, TimeUnit.SECONDS);}/** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @param unit 时间单位 * @return true=设置成功;false=设置失败 */public boolean expire(final String key, final long timeout, final TimeUnit unit){return redisTemplate.expire(key, timeout, unit);}/** * 获得缓存的基本对象。 * * @param key 缓存键值 * @return 缓存键值对应的数据 */public <T> T getCacheObject(final String key){ValueOperations<String, T> operation = redisTemplate.opsForValue();return operation.get(key);}/** * 删除单个对象 * * @param key */public boolean deleteObject(final String key){return redisTemplate.delete(key);}/** * 删除集合对象 * * @param collection 多个对象 * @return */public long deleteObject(final Collection collection){return redisTemplate.delete(collection);}/** * 缓存List数据 * * @param key 缓存的键值 * @param dataList 待缓存的List数据 * @return 缓存的对象 */public <T> long setCacheList(final String key, final List<T> dataList){Long count = redisTemplate.opsForList().rightPushAll(key, dataList);return count == null ? 0 : count;}/** * 获得缓存的list对象 * * @param key 缓存的键值 * @return 缓存键值对应的数据 */public <T> List<T> getCacheList(final String key){return redisTemplate.opsForList().range(key, 0, -1);}/** * 缓存Set * * @param key 缓存键值 * @param dataSet 缓存的数据 * @return 缓存数据的对象 */public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet){BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);Iterator<T> it = dataSet.iterator();while (it.hasNext()){setOperation.add(it.next());}return setOperation;}/** * 获得缓存的set * * @param key * @return */public <T> Set<T> getCacheSet(final String key){return redisTemplate.opsForSet().members(key);}/** * 缓存Map * * @param key * @param dataMap */public <T> void setCacheMap(final String key, final Map<String, T> dataMap){if (dataMap != null) {redisTemplate.opsForHash().putAll(key, dataMap);}}/** * 获得缓存的Map * * @param key * @return */public <T> Map<String, T> getCacheMap(final String key){return redisTemplate.opsForHash().entries(key);}/** * 往Hash中存入数据 * * @param key Redis键 * @param hKey Hash键 * @param value 值 */public <T> void setCacheMapValue(final String key, final String hKey, final T value){redisTemplate.opsForHash().put(key, hKey, value);}/** * 获取Hash中的数据 * * @param key Redis键 * @param hKey Hash键 * @return Hash中的对象 */public <T> T getCacheMapValue(final String key, final String hKey){HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();return opsForHash.get(key, hKey);}/** * 删除Hash中的数据 ** @param key * @param hkey */public void delCacheMapValue(final String key, final String hkey){HashOperations hashOperations = redisTemplate.opsForHash();hashOperations.delete(key, hkey);}/** * 获取多个Hash中的数据 * * @param key Redis键 * @param hKeys Hash键集合 * @return Hash对象集合 */public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys){return redisTemplate.opsForHash().multiGet(key, hKeys);}/** * 获得缓存的基本对象列表 * * @param pattern 字符串前缀 * @return 对象列表 */public Collection<String> keys(final String pattern){return redisTemplate.keys(pattern);}}

将字符串渲染到客户端工具类

import javax.servlet.http.HttpServletResponse;import java.io.IOException;public class WebUtils{/** * 将字符串渲染到客户端 ** @param response 渲染对象 * @param string 待渲染的字符串 * @return null */public static String renderString(HttpServletResponse response, String string) {try{response.setStatus(200);response.setContentType("application/json");response.setCharacterEncoding("utf-8");response.getWriter().print(string);}catch (IOException e){e.printStackTrace();}return null;}}

SQL语句创建用户表

CREATE TABLE `sys_user` (`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`user_name` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '用户名',`nick_name` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '昵称',`password` VARCHAR(64) NOT NULL DEFAULT 'NULL' COMMENT '密码',`status` CHAR(1) DEFAULT '0' COMMENT '账号状态(0正常 1停用)',`email` VARCHAR(64) DEFAULT NULL COMMENT '邮箱',`phonenumber` VARCHAR(32) DEFAULT NULL COMMENT '手机号',`sex` CHAR(1) DEFAULT NULL COMMENT '用户性别(0男,1女,2未知)',`avatar` VARCHAR(128) DEFAULT NULL COMMENT '头像',`user_type` CHAR(1) NOT NULL DEFAULT '1' COMMENT '用户类型(0管理员,1普通用户)',`create_by` BIGINT(20) DEFAULT NULL COMMENT '创建人的用户id',`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',`update_by` BIGINT(20) DEFAULT NULL COMMENT '更新人',`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',`del_flag` INT(11) DEFAULT '0' COMMENT '删除标志(0代表未删除,1代表已删除)',PRIMARY KEY (`id`)) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='用户表'

配置文件

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/你的数据库名称?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8username: ****password: ****redis:# Redis服务器地址host:127.0.0.1# Redis服务器连接端口port: 6379# Redis服务器连接密码password:# Redis数据库索引database: 3# 连接超时时间(毫秒)timeout: 30000lettuce:pool:max-active: 50max-wait: -1max-idle: 50min-idle: 1

编码实现

目录结构
关于Email的类不用管

配置类
RedisConfig类

package com.zzuli.common.config;import com.zzuli.common.utils.FastJsonRedisSerializer;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.StringRedisSerializer;public class RedisConfig {public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory){RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(connectionFactory);FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);// 使用StringRedisSerializer来序列化和反序列化redis的key值template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(serializer);// Hash的key也采用StringRedisSerializer的序列化方式template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(serializer);template.afterPropertiesSet();return template;}}

SecurityConfig

package com.zzuli.common.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.http.SessionCreationPolicy;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;/** * @author niuben */@Configurationpublic class SecurityConfig extends WebSecurityConfigurerAdapter {//创建BCryptPasswordEncoder注入容器@Beanpublic PasswordEncoder passwordEncoder(){return new BCryptPasswordEncoder();}@Overrideprotected void configure(HttpSecurity http) throws Exception {http//关闭csrf.csrf().disable()//不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 对于登录接口 允许匿名访问.antMatchers("/user/login").anonymous()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}}

实体类
User类

package com.zzuli.pojo;import com.baomidou.mybatisplus.annotation.TableId;import com.baomidou.mybatisplus.annotation.TableName;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.io.Serializable;import java.util.Date;/** * 用户表(User)实体类 * * @author 三更 */@Data@AllArgsConstructor@NoArgsConstructor@TableName("sys_user") //数据库的用户名名称public class User implements Serializable {private static final long serialVersionUID = -40356785423868312L;/*** 主键*/@TableIdprivate Long id;/*** 用户名*/private String userName;/*** 昵称*/private String nickName;/*** 密码*/private String password;/*** 账号状态(0正常 1停用)*/private String status;/*** 邮箱*/private String email;/*** 手机号*/private String phonenumber;/*** 用户性别(0男,1女,2未知)*/private String sex;/*** 头像*/private String avatar;/*** 用户类型(0管理员,1普通用户)*/private String userType;/*** 创建人的用户id*/private Long createBy;/*** 创建时间*/private Date createTime;/*** 更新人*/private Long updateBy;/*** 更新时间*/private Date updateTime;/*** 删除标志(0代表未删除,1代表已删除)*/private Integer delFlag;}

UserLogin
继承UserDetails

package com.zzuli.pojo;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;/** * @author niuben */@AllArgsConstructor@NoArgsConstructor@Datapublic class UserLogin implements UserDetails {private User user;@Overridepublic Collection<" />extends GrantedAuthority> getAuthorities() {return null;}@Overridepublic String getPassword() {return user.getPassword();}@Overridepublic String getUsername() {return user.getUserName();}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}}

接口实现类
LoginController接口

package com.zzuli.controller;import com.zzuli.common.api.CommonResult;import com.zzuli.pojo.User;import com.zzuli.service.LoginService;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/** * @author niuben */@RestControllerpublic class LoginController {@Resourceprivate LoginService loginService;@PostMapping("/user/login")public CommonResult<Object> login(@RequestBody User user){CommonResult<Object> result = null;try {result = loginService.login(user);} catch (Exception e) { return CommonResult.success("账户或者密码错误!");}return result;}}

UserDetailsServiceImpl实现类

package com.zzuli.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.zzuli.mapper.UserMapper;import com.zzuli.pojo.User;import com.zzuli.pojo.UserLogin;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;import javax.annotation.Resource;import java.util.Objects;/** * @author niuben */@Servicepublic class UserDetailsServiceImpl implements UserDetailsService {@Resourceprivate UserMapper userMapper;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//查询用户信息LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>();queryWrapper.eq(User::getUserName,username);User user = userMapper.selectOne(queryWrapper);//如果没有查询到用户,就抛出异常if(Objects.isNull(user)){throw new RuntimeException("用户名或者密码错误!");}//将数据封装成UserDetailsreturn new UserLogin(user);}}

LoginService接口

package com.zzuli.service;import com.zzuli.common.api.CommonResult;import com.zzuli.pojo.User;/** * @author niuben */public interface LoginService {CommonResult<Object> login(User user);}

LoginService实现接口

package com.zzuli.service.impl;import com.zzuli.common.api.CommonResult;import com.zzuli.common.utils.JwtUtil;import com.zzuli.common.utils.RedisCache;import com.zzuli.pojo.User;import com.zzuli.pojo.UserLogin;import com.zzuli.service.LoginService;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.stereotype.Service;import javax.annotation.Resource;import java.util.HashMap;import java.util.Map;import java.util.Objects;/** * @author niuben */@Servicepublic class LoginServiceImpl implements LoginService {@Resourceprivate AuthenticationManager authenticationManager;@Resourceprivate RedisCache redisCache;@Overridepublic CommonResult<Object> login(User user) {//进行用户认证UsernamePasswordAuthenticationToken authenticationToken =new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassword());Authentication authenticate = authenticationManager.authenticate(authenticationToken);//认证未通过,给出提示if(Objects.isNull(authenticate)){throw new RuntimeException("登陆失败!");}//通过了,生成jwtUserLogin loginUser = (UserLogin) authenticate.getPrincipal();String id = loginUser.getUser().getId().toString();String jwt = JwtUtil.createJWT(id);Map<String,String> map = new HashMap<>();map.put("token",jwt);//将用户信息存入redisredisCache.setCacheObject("login"+id,loginUser);return CommonResult.success(map,"登陆成功!");}}

UserMapper

package com.zzuli.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.zzuli.pojo.User;public interface UserMapper extends BaseMapper<User> {}

启动类

package com.zzuli;import org.apache.ibatis.annotations.Mapper;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("com.zzuli.mapper")//mapper扫描器public class AppleunlockManageApplication {public static void main(String[] args) {SpringApplication.run(AppleunlockManageApplication.class, args);}}

效果展示

注意:测试时要打开redis,数据库中的密码必须是加密后的
可以使用一下方法进行加密,passwordEncoderSecurityConfig中已经注入的

 @Resourceprivate PasswordEncoder passwordEncoder;String encode = passwordEncoder.encode("123456");System.out.println(encode);


登录接口测试