(一)SpringSecurity配合Jwt和Redis完成认证功能(配合之前的文章)

一、导入依赖包

 <!--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()));
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值