欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot結(jié)合JWT實現(xiàn)用戶登錄、注冊、鑒權(quán)

 更新時間:2023年05月11日 09:15:38   作者:JK凱  
用戶登錄、注冊及鑒權(quán)是我們基本所有系統(tǒng)必備的,也是很核心重要的一塊,本文主要介紹了SpringBoot結(jié)合JWT實現(xiàn)用戶登錄、注冊、鑒權(quán),具有一定的參考價值,感興趣的小伙伴們可以參考一下

用戶登錄、注冊及鑒權(quán)是我們基本所有系統(tǒng)必備的,也是很核心重要的一塊,這一塊的安全性等都比較重要,實現(xiàn)的方案其實也有幾種,從以前的cookie+session的方案,到現(xiàn)在常用的jwt的方案,這篇文章就講講目前在公司中最常用的jwt方案如何實現(xiàn)。

一、用戶注冊與登錄

完成用戶注冊與登錄有個核心點就是密碼的加密與驗證,我們目前比較常用的方案是密碼+鹽再采用MD5加密的方案,

鹽的方式一般可以在application.yml里面寫死,但安全性相對較差,還有就是通過UUID生成存到數(shù)據(jù)庫里,這里我們采用第二種安全性更高的方式。

sql如下:

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL,
  `salt` varchar(255) NOT NULL,
  `admin` int(1) DEFAULT '0',
  `age` int(3) NOT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  `deleted` int(1) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;

對應(yīng)的User實體類

domian.entity.User:

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@TableName("user")
public class User {
    @TableId
    private Long id;
    private String username;
    private String password;
    private String salt;
    private Boolean admin;
    private Integer age;
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date updateTime;
    private Integer deleted;
}

這里我們使用了Mybatis Plus的邏輯刪除及自動填充功能,不太清楚的可以看看我的文章SpringBoot 整合 Mybatis Plus 實現(xiàn)基本CRUD功能

接收用戶注冊信息的DTO

domain.dto.registryUserDto:

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.UUID;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class registryUserDto {
    private String username;
    private String password;
    @JsonIgnore
    private String salt = UUID.randomUUID().toString().replaceAll("-", "");
    private Boolean admin;
    private Integer age;
}

@JsonIgnore為忽略前端的傳值,這里使用我們UUID生成的值。

用戶登錄的DTO

domain.dto.LoginUserDto:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginUserDto {
    private String username;
    private String password;
}

用戶注冊與登錄的controller:

controller.UserController:

import com.jk.domain.dto.registryUserDto;
import com.jk.domain.dto.LoginUserDto;
import com.jk.service.UserService;
import com.jk.domain.vo.ResponseResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @PostMapping("/registry")
    public ResponseResult registryUser(@RequestBody registryUserDto registryUserDto) {
        return userService.registryUser(registryUserDto);
    }
    @PostMapping("/login")
    public ResponseResult login(@RequestBody LoginUserDto loginUserDto) {
        return userService.login(loginUserDto);
    }
}

用戶注冊與登錄的service:

service.UserService:

import com.jk.domain.dto.registryUserDto;
import com.jk.domain.dto.LoginUserDto;
import com.jk.domain.vo.ResponseResult;
public interface UserService {
    ResponseResult registryUser(registryUserDto registryUserDto);
    ResponseResult login(LoginUserDto loginUserDto);
}

用戶注冊與登錄的service實現(xiàn)類:

service.impl.UserServiceImpl:

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jk.domain.dto.registryUserDto;
import com.jk.domain.dto.LoginUserDto;
import com.jk.domain.entity.User;
import com.jk.enums.AppHttpCodeEnum;
import com.jk.mapper.UserMapper;
import com.jk.service.UserService;
import com.jk.domain.vo.ResponseResult;
import com.jk.utils.BeanCopyUtils;
import com.jk.utils.JwtUtils;
import com.jk.utils.RedisCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.util.concurrent.TimeUnit;
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private RedisCache redisCache;
    @Override
    public ResponseResult registryUser(registryUserDto registryUserDto) {
        String password = registryUserDto.getPassword();
        String salt = registryUserDto.getSalt();
        String md5Password = DigestUtils.md5DigestAsHex((password + salt).getBytes());
        registryUserDto.setPassword(md5Password);
        User user = BeanCopyUtils.copyBean(registryUserDto, User.class);
        userMapper.insert(user);
        return ResponseResult.okResult();
    }
    @Override
    public ResponseResult login(LoginUserDto loginUserDto) {
        String username = loginUserDto.getUsername();
        String password = loginUserDto.getPassword();
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        User user = userMapper.selectOne(queryWrapper);
        String md5Password = DigestUtils.md5DigestAsHex((password + user.getSalt()).getBytes());
        if (!md5Password.equals(user.getPassword())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_ERROR);
        }
        String token = JwtUtils.createToken(user.getId());
        redisCache.setCacheObject("TOKEN_" + token, JSON.toJSONString(user), 1, TimeUnit.DAYS);
        return ResponseResult.okResult(token);
    }
}

用戶注冊時,我們把密碼+salt進(jìn)行MD5加密,然后入庫,用戶登錄時,根據(jù)username查出用戶,再把用戶傳入的密碼+salt進(jìn)行MD5加密與數(shù)據(jù)庫查出的用戶進(jìn)行密碼比較判斷是否驗證通過。這里還有使用到一個JWT工具類,驗證通過后使用JWT工具類生成token和用戶信息存到redis里面,這里需要引入下fastjson來對用戶信息字符串化存,然后返回前端token。

具體JWT使用如下:

  • 首先引入fastjsonjwt的依賴包
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>2.0.26</version>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt</artifactId>
  <version>0.9.1</version>
</dependency>
  • JWT工具類的封裝

utils.JwtUtils:

import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class JwtUtils {
    private static final String jwtToken = "1234567890p[]l;'";
    public static String createToken(Long userId) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userId);
        JwtBuilder jwtBuilder = Jwts.builder()
                // 設(shè)置有效載荷
                .setClaims(claims)
                // 設(shè)置簽發(fā)時間
                .setIssuedAt(new Date())
                // 設(shè)置過期時間
                .setExpiration(new Date(System.currentTimeMillis() + 24 * 60 * 60 * 60 * 1000))
                // 采用HS256方式簽名,key就是用來簽名的秘鑰
                .signWith(SignatureAlgorithm.HS256, jwtToken);
        String token = jwtBuilder.compact();
        return token;
    }
    public static Map<String, Object> checkToken(String token) {
        try {
            Jwt parse = Jwts.parser().setSigningKey(jwtToken).parse(token);
            return (Map<String, Object>) parse.getBody();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

到此我們已經(jīng)完成了用戶的注冊和登錄功能。但還有一個問題就是用戶鑒權(quán),我們在調(diào)用其他接口時如何判斷用戶是否已登錄。

二、用戶鑒權(quán)

用戶鑒權(quán)我們需要用到ThreadLocal來存儲用戶信息,我們首先創(chuàng)建這個工具類

utils.UserThreadLocal:

import com.jk.domain.entity.User;
public class UserThreadLocal {
    private UserThreadLocal() {
    }
    private static final ThreadLocal<User> LOCAL = new ThreadLocal<>();
    public static void put(User user) {
        LOCAL.set(user);
    }
    public static User get() {
        return LOCAL.get();
    }
    public static void remove() {
        LOCAL.remove();
    }
}

還需要在service中實現(xiàn)驗證token的邏輯

service.UserService:

User checkToken(String token);

service.impl.UserServiceImpl:

@Override
public User checkToken(String token) {
    if (StringUtils.isEmpty(token)) {
        return null;
    }
    Map<String, Object> map = JwtUtils.checkToken(token);
    if (map == null) {
        return null;
    }
    String userJson =  redisCache.getCacheObject("TOKEN_" + token);
    if (StringUtils.isEmpty(userJson)) {
        return null;
    }
    User user = JSON.parseObject(userJson, User.class);
    return user;
}

使用攔截器實現(xiàn)token驗證

handler.interceptor.LoginInterceptor:

import com.jk.domain.entity.User;
import com.jk.enums.AppHttpCodeEnum;
import com.jk.exception.SystemException;
import com.jk.service.UserService;
import com.jk.utils.UserThreadLocal;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    @Autowired
    private UserService userService;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        String token = request.getHeader("token");
        log.info("===============request start===============");
        log.info("request uri:{}", request.getRequestURI());
        log.info("request method:{}", request.getMethod());
        log.info("token:{}", token);
        log.info("===============request end===============");
        if (StringUtils.isEmpty(token)) {
            throw new SystemException(AppHttpCodeEnum.NEED_LOGIN);
        }
        User user = userService.checkToken(token);
        if (user == null) {
            throw new SystemException(AppHttpCodeEnum.NEED_LOGIN);
        }
        UserThreadLocal.put(user);
        return true;
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        UserThreadLocal.remove();
    }
}

配置WebMvcConfigurer使用登錄攔截器

import com.jk.handler.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Autowired
    private LoginInterceptor loginInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/web/**")
                .addPathPatterns("/admin/**");
    }
}

會對/web/admin的所有接口做登錄驗證,這個大家根據(jù)自己項目需求調(diào)整。

到此這篇關(guān)于SpringBoot結(jié)合JWT實現(xiàn)用戶登錄、注冊、鑒權(quán)的文章就介紹到這了,更多相關(guān)SpringBoot JWT用戶登錄、注冊、鑒權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JVM的基本介紹以及垃圾回收

    JVM的基本介紹以及垃圾回收

    垃圾回收(Garbage Collection,GC),顧名思義就是釋放垃圾占用的空間,防止內(nèi)存泄露,這篇文章主要給大家介紹了關(guān)于JVM垃圾回收的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • java注解之運行時修改字段的注解值操作

    java注解之運行時修改字段的注解值操作

    這篇文章主要介紹了java注解之運行時修改字段的注解值操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • springboot 如何取消starter的自動注入

    springboot 如何取消starter的自動注入

    這篇文章主要介紹了springboot 如何取消starter的自動注入操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 項目連接nacos配置中心報錯:Client not connected, current status:STARTING的解決方案

    項目連接nacos配置中心報錯:Client not connected, current

    這篇文章主要介紹了項目連接nacos配置中心報錯:Client not connected, current status:STARTING的解決方案,采用了mysql作為持久化的數(shù)據(jù)庫,docker作為運行的環(huán)境,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • SpringBoot入坑筆記之spring-boot-starter-web 配置文件的使用

    SpringBoot入坑筆記之spring-boot-starter-web 配置文件的使用

    本篇向小伙伴介紹springboot配置文件的配置,已經(jīng)全局配置參數(shù)如何使用的。需要的朋友跟隨腳本之家小編一起學(xué)習(xí)吧
    2018-01-01
  • 深入分析JAVA 反射和泛型

    深入分析JAVA 反射和泛型

    這篇文章主要介紹了JAVA 反射和泛型的的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下
    2020-06-06
  • Spring Boot + Mybatis-Plus實現(xiàn)多數(shù)據(jù)源的方法

    Spring Boot + Mybatis-Plus實現(xiàn)多數(shù)據(jù)源的方法

    這篇文章主要介紹了Spring Boot + Mybatis-Plus實現(xiàn)多數(shù)據(jù)源的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • Spark操作之a(chǎn)ggregate、aggregateByKey詳解

    Spark操作之a(chǎn)ggregate、aggregateByKey詳解

    這篇文章主要介紹了Spark操作之a(chǎn)ggregate、aggregateByKey詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-06-06
  • JSP安全開發(fā)之XSS漏洞詳解

    JSP安全開發(fā)之XSS漏洞詳解

    XSS又叫CSS (Cross Site Script) ,跨站腳本攻擊。它指的是惡意攻擊者往Web頁面里插入惡意腳本代碼,而程序?qū)τ谟脩糨斎雰?nèi)容未過濾,當(dāng)用戶瀏覽該頁之時,嵌入其中Web里面的腳本代碼會被執(zhí)行,從而達(dá)到惡意攻擊用戶的特殊目的。
    2016-09-09
  • Java學(xué)習(xí)教程之定時任務(wù)全家桶

    Java學(xué)習(xí)教程之定時任務(wù)全家桶

    這篇文章主要給大家介紹了關(guān)于Java學(xué)習(xí)教程之定時任務(wù)全家桶的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11

最新評論