一、导入依赖包
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!--mybatis-plus generator-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3</version>
</dependency>
<!--freemarker模板引擎-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.31</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!--swagger文档注解-->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.6.2</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
<!--knife4j-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
<!-- SpringSecurity起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--jwt-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.18</version>
</dependency>
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
二、RedisCache工具类
package com.hubu.vue3store.util;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Component
public class RedisCache {
@Resource
public 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);
}
}
三、JwtUtils工具类
//视频教程:https://www.bilibili.com/video/BV1mm4y1X7Hc?p=18&spm_id_from=pageDriver&vd_source=4c11ab176d75ad5cbf02e8ed840601ed
//工具类:https://blog.csdn.net/qq_57147093/article/details/127650933
@Component
public class JwtUtils {
/**
* JWT_TTL 在这里设置的是时间 1000ms = 1s;则 60 * 1000*3L 这里为三分钟。
*/
public static final long JWT_TTL = 60 * 1000 * 30L;
public static final String JWT_KEY = "u2ui";
/**
* uuid 生成对随机数 作为id
* @return
*/
public static String getUUID() {
return UUID.randomUUID().toString().replaceAll("-", "");
}
/**
* 创建jwt构建器的方法
* @param subject
* @param ttlMillis
* @param uuid
* @return
*/
private JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
//生成密钥
SecretKey secretKey = generaKey();
long nowMillis = System.currentTimeMillis();
//生成系统当前时间
Date now = new Date(nowMillis);
//如果没有有效期,测采用默认有效期
if (ttlMillis == null) {
ttlMillis = JWT_TTL;
}
//生成到期时间
long expMillis = nowMillis + ttlMillis;
Date expDate = new Date(expMillis);
return Jwts.builder()
.setId(uuid) //随机数id
.setSubject(subject)//载荷(对象)
.setIssuer("sg")//作者
.setIssuedAt(now)//系统当前时间
.signWith(signatureAlgorithm, secretKey)//加密
.setExpiration(expDate);//有效期绝对时间
}
/**
* 生成默认有效期的token
* @param subject
* @return
*/
public String createJWT(String subject) {
return getJwtBuilder(subject, null, getUUID()).compact();
}
/**
* 生成指定有效期的token
* @param subject
* @param ttlMillis
* @return
*/
public String createJWT(String subject, Long ttlMillis) {
return getJwtBuilder(subject, ttlMillis, getUUID()).compact();
}
public String createJWT(String id, String subject, Long ttlMillis) {
return getJwtBuilder(subject, ttlMillis, id).compact();
}
/**
* 生成加密的密匙
* @return
*/
private static SecretKey generaKey() {
byte[] encodedKey = Base64.getDecoder().decode(JWT_KEY);
SecretKey key=new SecretKeySpec(encodedKey,0,encodedKey.length,"AES");
return key;
}
/**
* 解析token
* @param token
* @return
*/
public Claims parseJWT(String token) {
SecretKey key = generaKey();
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token)
.getBody();
}
}
四、封装统一请求结果类
1、IResultCode
public interface IResultCode {
/**
* 获取状态码
*
* @return 状态码
*/
Integer getCode();
/**
* 获取返回消息
*
* @return 返回消息
*/
String getMessage();
}
2、ResultCode
@Getter
public enum ResultCode implements IResultCode {
/**
* 成功
*/
OK(200, "成功"),
/**
* 失败
*/
ERROR(500, "失败"),
SYSTEM_ERROR(501, "系统错误"),
UNKNOWN_ERROR(502, "未知错误"),
/* 参数错误:1000~1999 */
PARAM_NOT_VALID(1001, "参数无效"),
PARAM_IS_BLANK(1002, "参数为空"),
PARAM_TYPE_ERROR(1003, "参数类型错误"),
PARAM_NOT_COMPLETE(1004, "参数缺失"),
PARAM_TYPE_REPEAT(1005,"参数重复"),
/* 用户错误 */
USER_NOT_LOGIN(2001, "用户未登录"),
USER_ACCOUNT_EXPIRED(2002, "账号已过期"),
USER_CREDENTIALS_ERROR(2003, "密码错误"),
USER_CREDENTIALS_EXPIRED(2004, "密码过期"),
USER_ACCOUNT_DISABLE(2005, "账号不可用"),
USER_ACCOUNT_LOCKED(2006, "账号被锁定"),
USER_ACCOUNT_NOT_EXIST(2007, "账号不存在"),
USER_ACCOUNT_NOT_NULL(2013, "账号不能为空"),
USER_CREDENTIALS_NOT_NULL(2014, "密码不能为空"),
USER_ACCOUNT_ALREADY_EXIST(2008, "账号已存在"),
USER_ACCOUNT_USE_BY_OTHERS(2009, "账号下线"),
PASSWORD_SAME(2010, "新密码与原密码一致"),
ACCOUNT_OR_PASSWORD_NOT_NULL(2016, "账号或者密码不能为空"),
/* token错误 */
TOKEN_IS_NULL(2011, "token不能为空"),
TOKEN_INVALID(2012, "token失效"),
TOKEN_ERROR(2015, "token错误"),
/* 业务错误 */
NO_PERMISSION(3001, "没有权限"),
ADD_FAIL(3002, "新增失败"),
UPDATE_FAIL(3003, "更新失败"),
DELETE_FAIL(3004, "删除失败"),
QUERY_FAIL(3005, "查询失败"),
QUERY_EMPTY(3006, "查询为空"),
QUERY_NOT_EXIST(3007, "查询不存在"),
EMAIL_ERROR(2060, "邮箱格式错误"),
PHONE_ERROR(2061, "电话格式错误"),
EMAIL_REPEAT_ERROR(2062, "邮箱已存在"),
/* 文件读写 */
FILE_IS_NULL(2041, "文件为空"),
FILE_WRONG_FORMAT(2042, "文件格式错误"),
FILE_CHUNK_UPLOAD_ERROR(2043, "文件分块上传失败!"),
FILE_CHUNK_MERGE_ERROR(2044, "文件分块合并失败"),
FILE_CONTENT_ERROR(2045, "上传文件内容错误"),
/** 数据导出 */
EXPORT_FAILURE(2100, "导出失败"),
/** 数据导入 */
IMPORT_FAILURE(2101,"导入失败");
/**
* 返回码
*/
private Integer code;
/**
* 返回消息
*/
private String message;
ResultCode(Integer code, String message) {
this.code = code;
this.message = message;
}
}
3、Result
@Data
@NoArgsConstructor
public class Result<T> {
/** 状态码 */
private Integer code;
/** 返回消息 */
private String message;
/** 状态 */
private boolean status;
/** 返回数据 */
private T data;
public Result(Integer code, String message, boolean status, T data) {
this.code = code;
this.message = message;
this.status = status;
this.data = data;
}
public Result(ResultCode resultCode, boolean status, T data) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
this.status = status;
this.data = data;
}
public Result(ResultCode resultCode, boolean status) {
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
this.status = status;
this.data = null;
}
public static <T> Result success() {
return new Result<>(ResultCode.OK, true);
}
public static <T> Result message(String message) {
return new Result<>(ResultCode.OK.getCode(), message, true, null);
}
public static <T> Result success(T data) {
return new Result<>(ResultCode.OK, true, data);
}
public static <T> Result fail() {
return new Result<>(ResultCode.ERROR, false);
}
public static <T> Result fail(ResultCode resultCode) {
return new Result<>(resultCode, false);
}
public static <T> Result fail(Integer code, String message) {
return new Result<>(code, message, false, null);
}
public static <T> Result fail(ResultCode resultCode, T data) {
return new Result<>(resultCode, false, data);
}
public static <T> Result fail(Integer code, String message, T data) {
return new Result<>(code, message, false, data);
}
}
五、创建WebSecurityConfig
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
六、创建自定义的SecurityUser类实现UserDetails接口
@AllArgsConstructor
@NoArgsConstructor
@Data
public class SecurityUser implements UserDetails {
private User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
//不使用会提示,并且验证不成功
return new BCryptPasswordEncoder().encode(user.getPassword());
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
七、创建UserDetailServiceImpl类实现UserDetailsService接口
@Service
public class UserDetailServiceImpl implements UserDetailsService {
@Resource
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名查询用户信息
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUsername,username);
User user = userMapper.selectOne(queryWrapper);
return new SecurityUser(user);
}
}
八、创建登录ILoginService和LoginServiceImpl类
//ILoginService
public interface ILoginService {
Object login(String username, String password);
}
//LoginServiceImpl
@Service
@Slf4j
public class LoginServiceImpl implements ILoginService {
@Resource
private AuthenticationManager authenticationManager;
@Resource
private JwtUtils jwtUtils;
@Resource
private RedisCache redisCache;
@Override
public String login(String username, String password) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password);
System.out.println("authentication,"+authentication);
Authentication authenticate = authenticationManager.authenticate(authentication);
System.out.println("authenticate"+authenticate);
//如果没有认证成功
if(!authenticate.isAuthenticated()){
throw new UsernameNotFoundException("请重新检查用户名和密码!");
}
//认证成功
//1、生成token并返回
SecurityUser securityUser = (SecurityUser)authenticate.getPrincipal();
String uid = securityUser.getUser().getUid().toString();
String token = jwtUtils.createJWT(uid);
Map<String, String> map = new HashMap<>();
map.put("token",token);
//保存进redis
redisCache.setCacheObject("login:"+uid,securityUser);
return map;
}
}
九、创建LoginController.java
@RestController
public class LoginController {
@Resource
private ILoginService loginService;
/**
* <h1>自定义登录接口</h1>
* @param user
* @return
*/
@PostMapping("/login")
public Result login(@RequestBody User user) {
return Result.success(loginService.login(user.getUsername(),user.getPassword()));
}
}