精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Spring Boot Security + JWT Token 的簡單應用

開發 前端
我們在Spring Boot示例中學到關于Spring Security和基于JWT令牌的身份驗證的有趣知識。盡管我們寫了很多代碼,但我希望你能理解應用程序的整體架構,并輕松地將其應用到你的項目中。

今天主要介紹以下內容:

  • 用戶可以注冊新帳戶,或使用用戶名和密碼登錄。
  • 根據用戶的權限,我們授權用戶訪問資源

今日內容介紹,大約花費40分鐘

圖片圖片

1.Spring Boot 注冊和登錄with JWT 身份驗證流程

下圖顯示了我們如何實現用戶注冊、用戶登錄和授權流程的流程。

圖片圖片

如果客戶端訪問受保護的資源,則必須將合法的 JWT 添加到 HTTP 授權標頭中。

Spring Boot中使用Spring Security

您可以通過下圖概述我們的Spring Boot項目:

圖片圖片

Spring Security介紹:

  • WebSecurityConfig: spring Security 配置類,用于配置 Spring Security 的行為和規則。它為受保護的資源配置 cors、csrf、會話管理、規則。我們還可以擴展和自定義包含以下元素的默認配置。這個類通常擴展自 WebSecurityConfigurerAdapter

注意:WebSecurityConfigurerAdapter 從 SpringBoot 2.7.0 開始被棄用)

  • UserDetailsService: UserDetailsService 是 Spring Security 中的一個接口,用于從數據源加載用戶的詳細信息,并返回一個 UserDetails 對象。UserDetails 包含有關用戶的各種信息,例如用戶名、密碼、權限等。
  • UserDetails: 實體類對象,包含構建 Authentication 對象所需的信息(例如:用戶名、密碼、權限)。
  • UsernamePasswordAuthenticationToken: 從登錄請求中獲取 {username, password}, AuthenticationManager 將使用它來驗證登錄帳戶。
  • AuthenticationManager: 有一個DaoAuthenticationProvider (借助 UserDetailsService & PasswordEncoder )來驗證 UsernamePasswordAuthenticationToken 對象。如果成功, AuthenticationManager 則返回完全填充的 Authentication 對象(包括授予的權限)。
  • OncePerRequestFilter: 對 API 的每個請求進行一次執行。它提供了一種 doFilterInternal() 方法,我們將實現解析和驗證JWT,加載用戶詳細信息(使用),檢查授權(使用 UserDetailsService UsernamePasswordAuthenticationToken )。
  • AuthenticationEntryPoint: 捕獲身份驗證錯誤。

2.項目準備

下圖是Spring Boot項目的文件夾和文件結構:

圖片圖片

圖片圖片

  • UserDetailsServiceImpl 實現 UserDetailsService
  • AuthEntryPointJwt 實現 AuthenticationEntryPoint
  • AuthTokenFilter 延伸 OncePerRequestFilter
  • JwtUtils 提供用于生成、解析和驗證 JWT 的方法
  • 還有 application.yml,用于配置 Spring Datasource、Mybatis-Plus和項目屬性(例如 JWT Secret 字符串或 Token 過期時間)

2.1. 創建表

根據Sql創建表,表間關系如下:

圖片圖片

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for pe_permission
-- ----------------------------
DROP TABLE IF EXISTS `pe_permission`;
CREATE TABLE `pe_permission` (
                                 `id` varchar(40) NOT NULL COMMENT '主鍵',
                                 `name` varchar(255) DEFAULT NULL COMMENT '權限名稱',
                                 `code` varchar(20) DEFAULT NULL,
                                 `description` text COMMENT '權限描述',
                                 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of pe_permission
-- ----------------------------
INSERT INTO `pe_permission` VALUES ('1', '添加用戶', 'user-add', null);
INSERT INTO `pe_permission` VALUES ('2', '查詢用戶', 'user-find', null);
INSERT INTO `pe_permission` VALUES ('3', '更新用戶', 'user-update', null);
INSERT INTO `pe_permission` VALUES ('4', '刪除用戶', 'user-delete', null);

-- ----------------------------
-- Table structure for pe_role
-- ----------------------------
DROP TABLE IF EXISTS `pe_role`;
CREATE TABLE `pe_role` (
                           `id` varchar(40) NOT NULL COMMENT '主鍵ID',
                           `name` varchar(40) DEFAULT NULL COMMENT '權限名稱',
                           `description` varchar(255) DEFAULT NULL COMMENT '說明',
                           PRIMARY KEY (`id`),
                           UNIQUE KEY `UK_k3beff7qglfn58qsf2yvbg41i` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of pe_role
-- ----------------------------
INSERT INTO `pe_role` VALUES ('1', '系統管理員', '系統日常維護');
INSERT INTO `pe_role` VALUES ('2', '普通員工', '普通操作權限');

-- ----------------------------
-- Table structure for pe_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `pe_role_permission`;
CREATE TABLE `pe_role_permission` (
                                      `role_id` varchar(40) NOT NULL COMMENT '角色ID',
                                      `permission_id` varchar(40) NOT NULL COMMENT '權限ID',
                                      PRIMARY KEY (`role_id`,`permission_id`),
                                      KEY `FK74qx7rkbtq2wqms78gljv87a0` (`permission_id`),
                                      KEY `FKee9dk0vg99shvsytflym6egxd` (`role_id`),
                                      CONSTRAINT `fk-p-rid` FOREIGN KEY (`role_id`) REFERENCES `pe_role` (`id`),
                                      CONSTRAINT `fk-pid` FOREIGN KEY (`permission_id`) REFERENCES `pe_permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of pe_role_permission
-- ----------------------------
INSERT INTO `pe_role_permission` VALUES ('1', '1');
INSERT INTO `pe_role_permission` VALUES ('1', '2');
INSERT INTO `pe_role_permission` VALUES ('2', '2');
INSERT INTO `pe_role_permission` VALUES ('1', '3');
INSERT INTO `pe_role_permission` VALUES ('1', '4');

-- ----------------------------
-- Table structure for pe_user
-- ----------------------------
DROP TABLE IF EXISTS `pe_user`;
CREATE TABLE `pe_user` (
                           `id` varchar(40) NOT NULL COMMENT 'ID',
                           `username` varchar(255) NOT NULL COMMENT '用戶名稱',
                           `password` varchar(255) DEFAULT NULL COMMENT '密碼',
                           PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of pe_user
-- ----------------------------
INSERT INTO `pe_user` VALUES ('1', 'zhangsan', '$2a$10$.fXoccJHJkb9KM1FYJd1Ve.2P0B0RgLvloBDPwGjRxcP2obt2NRkG');
INSERT INTO `pe_user` VALUES ('2', 'lisi', '$2a$10$.fXoccJHJkb9KM1FYJd1Ve.2P0B0RgLvloBDPwGjRxcP2obt2NRkG');
INSERT INTO `pe_user` VALUES ('3', 'wangwu', '$2a$10$.fXoccJHJkb9KM1FYJd1Ve.2P0B0RgLvloBDPwGjRxcP2obt2NRkG');

-- ----------------------------
-- Table structure for pe_user_role
-- ----------------------------
DROP TABLE IF EXISTS `pe_user_role`;
CREATE TABLE `pe_user_role` (
                                `role_id` varchar(40) NOT NULL COMMENT '角色ID',
                                `user_id` varchar(40) NOT NULL COMMENT '權限ID',
                                KEY `FK74qx7rkbtq2wqms78gljv87a1` (`role_id`),
                                KEY `FKee9dk0vg99shvsytflym6egx1` (`user_id`),
                                CONSTRAINT `fk-rid` FOREIGN KEY (`role_id`) REFERENCES `pe_role` (`id`),
                                CONSTRAINT `fk-uid` FOREIGN KEY (`user_id`) REFERENCES `pe_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of pe_user_role
-- ----------------------------
INSERT INTO `pe_user_role` VALUES ('1', '1');

2.2. 在pom.xml中添加依賴

<?xml versinotallow="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.7.15</version>
    </parent>

    <groupId>com.zbbmeta</groupId>
    <artifactId>spring-boot-backend-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>


        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.20</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-configuration-processor -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>

        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>


    </dependencies>

</project>

2.3. 使用MyBatisX插件生成代碼

安裝MyBatisX插件,這里就不過多介紹如何安裝插件了,和其他插件安裝相同 表生成代碼步驟如下:

  • 選擇表右鍵選擇MybatisX-Generator

圖片圖片

  • 選擇代碼生成位置

圖片圖片

  • 選擇生成代碼的規則

圖片圖片

  • 我們根據規則將以下表進行生成:
  • pe_permission
  • pe_role
  • pe_role_permission
  • pe_user
  • pe_user_role

2.4. 創建UserDetailsService實現類

Spring Security 將加載用戶詳細信息以執行身份驗證和授權。所以它有 UserDetailsService 我們需要實現的接口。

在com.zbbmeta.service.impl包下創建UserDetailsService實現類UserDetailsServiceImpl

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByName(username);
        List<Role> roles = user.getRoles();
        List<String> authorities = new ArrayList<>();
        for (Role role : roles) {
            List<Permission> permissions = role.getPermissions();

            List<String> collect = permissions.stream().map(x -> x.getCode()).distinct().collect(Collectors.toList());
            authorities.addAll(collect);
        }


        List<String> collect1 = authorities.stream().distinct().collect(Collectors.toList());
        return   new org.springframework.security.core.userdetails.User(username, user.getPassword(), AuthorityUtils.createAuthorityList(collect1.toString()));

    }
}

2.5. 在Mapper類中添加查詢用戶和查詢權限方法

  • 在UserMapper添加根據用戶名查詢用戶方法
public interface UserMapper extends BaseMapper<User> {

    User findByUsername(String name);
}
  • 在UserMapper.xml添加方法的實現

在User實體類中添加字段roles

@TableField(exist = false)
    private List<Role> roles = new ArrayList<>();//用戶與角色   多對多

圖片圖片

并且在resultMap添加一對多根據用戶查詢對應角色

<?xml versinotallow="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zbbmeta.mapper.UserMapper">

    <resultMap id="BaseResultMap" type="com.zbbmeta.entity.User">
            <id property="id" column="id" jdbcType="VARCHAR"/>
            <result property="username" column="username" jdbcType="VARCHAR"/>
            <result property="password" column="password" jdbcType="VARCHAR"/>

        <!--一對多映射用這個  ofTyp是一對多的集合的所存放的實體類  javaType實體類的屬性類型-->
        <collection property="roles" ofType="com.zbbmeta.entity.Role"
                    select="com.zbbmeta.mapper.RoleMapper.queryRoleListByUserId" column="id">
            <id property="id" column="id" jdbcType="VARCHAR"/>
            <result property="name" column="name" jdbcType="VARCHAR"/>
            <result property="description" column="description" jdbcType="VARCHAR"/>
        </collection>

    </resultMap>

    <sql id="Base_Column_List">
        id,username,password
    </sql>

    <select id="findByUsername" resultMap="BaseResultMap">
        select * from pe_user where username=#{name};
    </select>
</mapper>
  • 在RoleMapper添加根據id查詢角色
public interface RoleMapper extends BaseMapper<Role> {
    List<Role> queryRoleListByUserId(@Param("id") Long id);
}
  • 在RoleMapper.xml添加方法的實現 在Role實體類中添加字段permissions
@TableField(exist = false)
    private List<Permission> permissions = new ArrayList<>();//用戶與角色   多對多

圖片圖片

并且在resultMap添加一對多根據角色查詢對應權限

<?xml versinotallow="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zbbmeta.mapper.RoleMapper">

    <resultMap id="BaseResultMap" type="com.zbbmeta.entity.Role">
            <id property="id" column="id" jdbcType="VARCHAR"/>
            <result property="name" column="name" jdbcType="VARCHAR"/>
            <result property="description" column="description" jdbcType="VARCHAR"/>
        <collection property="permissions" ofType="com.zbbmeta.entity.Permission"
                    select="com.zbbmeta.mapper.PermissionMapper.queryPermissionList" column="id">
            <id property="id" column="id" jdbcType="VARCHAR"/>
            <result property="name" column="name" jdbcType="VARCHAR"/>
            <result property="code" column="code" jdbcType="VARCHAR"/>
            <result property="description" column="description" jdbcType="VARCHAR"/>
        </collection>
    </resultMap>

    <sql id="Base_Column_List">
        id,name,description
    </sql>

    <select id="queryRoleListByUserId" resultMap="BaseResultMap">
        select distinct pr.* from pe_user_role pur
                                      inner join pe_role pr on pur.role_id =pr.id
        where pur.user_id =#{id}
    </select>
</mapper>
  • 在PermissionMapper添加根據id查詢權限
public interface PermissionMapper extends BaseMapper<Permission> {
    List<Permission> queryPermissionList(@Param("id") Long id);
}
  • 在PermissionMapper.xml添加方法的實現
<?xml versinotallow="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zbbmeta.mapper.PermissionMapper">

    <resultMap id="BaseResultMap" type="com.zbbmeta.entity.Permission">
            <id property="id" column="id" jdbcType="VARCHAR"/>
            <result property="name" column="name" jdbcType="VARCHAR"/>
            <result property="code" column="code" jdbcType="VARCHAR"/>
            <result property="description" column="description" jdbcType="VARCHAR"/>


    </resultMap>

    <sql id="Base_Column_List">
        id,name,code,
        description
    </sql>

    <select id="queryPermissionList" resultType="com.zbbmeta.entity.Permission">
        select * from pe_permission pp
                          inner join pe_role_permission prp on pp.id = prp.permission_id
        where prp.role_id=#{id}
    </select>
</mapper>

3. 配置 Spring Security

注意:不使用 WebSecurityConfigurerAdapter,因為WebSecurityConfigurerAdapter 從 Spring 2.7.0 中棄用

【步驟一】: 在application.yml 添加jwt配置

jwt:
  config:
    key: zbbmeta
    ttl: 3600

【步驟一】: 創建JwtUtil工具類

在com.zbbmeta.util包下創建JwtUtil類

package com.zbbmeta.util;

import io.jsonwebtoken.*;
import lombok.Data;
import org.junit.platform.commons.logging.Logger;
import org.junit.platform.commons.logging.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.Map;

/**
 * @author springboot葵花寶典
 * @description: TODO
 */
@Component
@ConfigurationProperties("jwt.config")
@Data
public class JwtUtil {

    private String key;
    private long ttl;


    public String createJwt(String id, String subject, Map<String,Object> map){
        long now = System.currentTimeMillis();
        long exp = now+ttl*1000;
        JwtBuilder jwtBuilder =null;
        try {
            jwtBuilder = Jwts.builder().setId(id)
                    .setSubject(subject)
                    .setIssuedAt(new Date())
                    .signWith(SignatureAlgorithm.HS256, key);

            for (Map.Entry<String, Object> stringObjectEntry : map.entrySet()) {
                jwtBuilder.claim(stringObjectEntry.getKey(), stringObjectEntry.getValue());
            }
            if (ttl > 0) {
                jwtBuilder.setExpiration(new Date(exp));
            }
        }catch (Exception e){
            System.err.println(e.getMessage());
        }

        return jwtBuilder.compact();

    }


    public Claims parseJWT(String token){

        Claims     claims = Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(token).getBody();

        return claims;
    }

}

【步驟二】:創建 WebSecurityConfig 類

WebSecurityConfig類 是我們安全認證的關鍵。它為受保護的資源配置 cors、csrf、會話管理、規則。

package com.zbbmeta.config;

import com.zbbmeta.filter.AuthTokenFilter;
import com.zbbmeta.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author springboot葵花寶典
 * @description: TODO
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Autowired
    UserDetailsServiceImpl userDetailsService;

    @Autowired
    private AuthEntryPointJwt unauthorizedHandler;


    @Bean
    public AuthTokenFilter authTokenFilter() {
        return new AuthTokenFilter();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();

        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(passwordEncoder());

        return authProvider;
    }

    /**
     * 認證
     * @param http
     * @return
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests().antMatchers("/api/auth/**").permitAll()//不需要通過登錄驗證就可以被訪問的資源路徑
                .anyRequest().authenticated();
        http.authenticationProvider(authenticationProvider());

        http.addFilterBefore(authTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

}

注: 同樣的在 Spring Security 5.8 后要把  antMatchers() 改為 requestMatchers()

  • @EnableWebSecurity 允許 Spring 查找并自動將該類應用于全局 Web 安全。
  • AuthenticationManagerBuilder.userDetailsService() 方法進行配置 DaoAuthenticationProvider 。并且我們還需要一個 PasswordEncoder對密碼進行加密 .如果我們不指定,它將使用純文本。

【步驟三】:創建過濾器篩選請求

在 com.zbbmeta.filter包下創建AuthTokenFilter類,對每個請求進行過濾。 AuthTokenFilter 集成 OncePerRequestFilter 和覆蓋 doFilterInternal() 方法的類。

@Slf4j
public class AuthTokenFilter extends OncePerRequestFilter {

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserDetailsService userDetailsService;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestURI = request.getRequestURI();
        //登錄或者注冊放行
        if(requestURI.contains("/api/auth")){
            filterChain.doFilter(request, response);
            return ;
        }
        String authorization = request.getHeader("Authorization");
        if(StrUtil.isNotBlank(authorization)){
            String token = authorization.replace("Bearer ", "");
            Claims claims = jwtUtil.parseJWT(token);
            String username = claims.get("username", String.class);
            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken authentication =
                    new UsernamePasswordAuthenticationToken(
                            userDetails,
                            null,
                            userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

            SecurityContextHolder.getContext().setAuthentication(authentication);
        }


        filterChain.doFilter(request, response);

    }
}

我們在里面 doFilterInternal() 做什么:

  • 從 Authorization 標頭獲取 JWT (通過刪除 Bearer 前綴)
  • – 如果請求有 JWT ,請驗證它, 將username 從token中解析

【步驟四】:處理身份驗證異常

創建 AuthEntryPointJwt 實現接口的 AuthenticationEntryPoint 類。然后我們重寫該 commence() 方法。每當未經身份驗證的用戶請求受保護的 HTTP 資源并 AuthenticationException 拋出時,都會觸發此方法。

package com.zbbmeta.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author springboot葵花寶典
 * @description: TODO
 */
@Slf4j
@Component
public class AuthEntryPointJwt implements AuthenticationEntryPoint {



    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
            throws IOException, ServletException {
        log.error("Unauthorized error: {}", authException.getMessage());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);

        final Map<String, Object> body = new HashMap<>();
        body.put("code", HttpServletResponse.SC_UNAUTHORIZED);

        body.put("message", authException.getMessage());
        body.put("path", request.getServletPath());

        final ObjectMapper mapper = new ObjectMapper();
        mapper.writeValue(response.getOutputStream(), body);

    }
}

【步驟五】:創建Controller進行登錄和注冊

  • /api/auth/signup: 注冊用戶
  • 檢查現有 username
  • 新建 User
  • 保存 User 到數據庫
  • /api/auth/signin:用戶登錄
package com.zbbmeta.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zbbmeta.api.Result;
import com.zbbmeta.api.ResultCode;
import com.zbbmeta.dto.LoginDto;
import com.zbbmeta.dto.SignupDto;
import com.zbbmeta.entity.Permission;
import com.zbbmeta.entity.Role;
import com.zbbmeta.entity.User;
import com.zbbmeta.entity.UserRole;
import com.zbbmeta.service.RoleService;
import com.zbbmeta.service.UserRoleService;
import com.zbbmeta.service.UserService;
import com.zbbmeta.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
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 java.util.*;
import java.util.stream.Collectors;

/**
 * @author springboot葵花寶典
 * @description: TODO
 */
@RequestMapping("/api/auth")
@RestController
public class AuthController {

    @Autowired
    UserService userService;

    @Autowired
    RoleService roleService;

    @Autowired
    UserRoleService userRoleService;

    @Autowired
    PasswordEncoder passwordEncoder;
    @Autowired
    private JwtUtil jwtUtil;

    @PostMapping("/signin")
    public Result authenticateUser(@RequestBody LoginDto loginDto) {
        //根據用戶名查找用戶
        User user = userService.findByName(loginDto.getUsername());
        //不存在表示登錄失敗
        if(Objects.isNull(user)){
            return Result.FAIL(ResultCode.USERNOEXIT_ERROR);
        }
        //密碼不同登錄失敗
        if(!passwordEncoder.matches(loginDto.getPassword(),user.getPassword())){
            return Result.FAIL(ResultCode.PASSWORD_ERROR);
        }
        List<String> collect = user.getRoles().stream().map(x -> x.getName()).collect(Collectors.toList());
        StringBuilder builder = new StringBuilder();
        List<Role> roles = user.getRoles();
        for (Role role : roles) {
            List<Permission> permissions = role.getPermissions();
            for (Permission permission : permissions) {

                builder.append(permission.getCode()).append(",");
            }
        }
        Map<String, Object> map  = new HashMap<>();
        map.put("username",user.getUsername());
        map.put("permission",builder);
        String token = jwtUtil.createJwt(user.getId(), user.getUsername(), map);

        return Result.SUCCESS(token);
    }



    /**
     * 用戶注冊
     * @param signupDto
     * @return
     */
    @PostMapping("/signup")
    public Result registerUser( @RequestBody SignupDto signupDto){
        //根據用戶名獲取用戶
        User user = userService.findByName(signupDto.getUsername());
        //用戶不是null表示用戶已經存在
        if(Objects.nonNull(user)){
            return Result.FAIL(ResultCode.USER_ERROR);
        }
        //添加用戶
        User user1 = new User();
        user1.setUsername(signupDto.getUsername());
        user1.setPassword(signupDto.getPassword());

        user1.setPassword(passwordEncoder.encode(signupDto.getPassword()));

        List<String> strRoles = signupDto.getRole();
        List<Role> roles = new ArrayList<>();
        //如果沒有用戶角色,默認添加普通員工
        if (strRoles == null) {
            LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Role::getName,"普通員工");
            Role role = roleService.getOne(wrapper);
            roles.add(role);
        }else {
            strRoles.forEach(role ->{
                LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
                wrapper.eq(Role::getName,role);
                Role adminRole = roleService.getOne(wrapper);
                roles.add(adminRole);
            });

        }
        //添加用戶信息
        boolean save = userService.save(user1);
        String id = user1.getId();

        List<UserRole> userRolesList = new ArrayList<>();
        for (Role role : roles) {
            UserRole userRoles = new UserRole();
            userRoles.setUserId(id);
            userRoles.setRoleId(role.getId());
            userRolesList.add(userRoles);
        }
        //添加用戶和角色關系
        userRoleService.saveBatch(userRolesList);
        return Result.SUCCESS("注冊成功!");

    }
}

4.測試

  • 使用PostMan進行用戶注冊

圖片圖片

注冊后的pe_user表格數據如下所示:

圖片圖片

  • 訪問受保護的資源:GET /api/tutorials/published

圖片圖片

因為我們沒有登錄,所以受保護的資源不能訪問

  • 登錄賬號:POST /api/auth/signin

圖片圖片

  • 復制Token:重新訪問受保護資源

圖片圖片

今天,我們在Spring Boot示例中學到關于Spring Security和基于JWT令牌的身份驗證的有趣知識。盡管我們寫了很多代碼,但我希望你能理解應用程序的整體架構,并輕松地將其應用到你的項目中。

## 代碼地址
https://github.com/bangbangzhou/spring-boot-backend-example.git
責任編輯:武曉燕 來源: springboot葵花寶典
相關推薦

2022-05-25 09:00:00

令牌JWT安全

2021-07-09 06:48:29

Spring Boot應用Keycloak

2021-12-28 11:13:05

安全認證 Spring Boot

2025-05-23 09:38:54

JWT開發Go

2021-04-26 08:54:17

Spring BootSecurity防重登錄

2017-09-20 09:46:38

Spring BootSpring Clou內存

2021-04-23 07:33:10

SpringSecurity單元

2023-03-16 08:14:57

2020-11-10 09:19:23

Spring BootJava開發

2021-08-09 08:53:30

HTTP狀態化協議

2025-09-23 09:32:30

2021-07-02 10:10:55

SecurityJWT系統

2025-09-15 09:34:45

2025-05-21 09:51:11

.NETJWTWeb

2021-09-28 06:57:22

JWT過期生效

2020-10-20 07:49:00

JWT(JSON We

2025-02-20 08:18:12

JWTWeb分布式

2024-09-30 11:51:07

2018-05-31 15:30:11

JavaSpring Boot應用開發

2020-10-27 09:00:00

NodeJS實現JWT
點贊
收藏

51CTO技術棧公眾號

欧美国产日韩精品| 在线观看视频一区| 久久久久久草| 超碰在线免费97| 久久久久国产精品| 亚洲国产日韩欧美在线图片| 粉嫩虎白女毛片人体| 黄色小网站在线观看| 高清视频一区二区| 国产a∨精品一区二区三区不卡| 999精品视频在线观看播放 | 国产欧美精品一区二区三区-老狼| 亚洲最大的黄色网址| 亚洲精品国模| 日韩一区二区中文字幕| 爱福利视频一区二区| 麻豆影视在线观看_| 久久影院午夜论| 亚洲最大成人免费视频| 波多野结衣一本一道| 国内精品久久久久久久影视麻豆| 亚洲精品国产精品国自产在线| 色www免费视频| 色黄视频在线观看| 亚洲综合一区在线| 亚洲乱码一区二区三区| 日韩一区av| 国产成人av电影免费在线观看| 国产日韩在线亚洲字幕中文| 在线观看黄网站| 国产精品激情| 欧美久久精品一级黑人c片| japanese中文字幕| 麻豆一区二区麻豆免费观看| 欧美一级搡bbbb搡bbbb| 一起操在线视频| 国产精品亚洲d| 欧美视频精品一区| 欧美一级欧美一级| 欧美韩日亚洲| 一个色妞综合视频在线观看| 天天干天天色天天爽| 97超碰人人在线| 国产精品日产欧美久久久久| 欧美主播一区二区三区美女 久久精品人 | 欧美偷拍一区二区| aaa毛片在线观看| 久久人体大尺度| 色视频一区二区| 成人在线免费播放视频| 欧美二三四区| 91黄色激情网站| 性生交免费视频| a屁视频一区二区三区四区| 在线视频你懂得一区| 男女视频一区二区三区| 成人视屏在线观看| 欧美性做爰猛烈叫床潮| 天堂网在线免费观看| 久久精品资源| 欧美mv日韩mv国产网站| 国产艳妇疯狂做爰视频| 久久中文资源| 亚洲精品视频免费在线观看| 99精品欧美一区二区| 91欧美在线| 欧美精品在线第一页| 国产精品第二十页| 模特精品在线| 国产欧美日韩91| jlzzjlzzjlzz亚洲人| 成人免费毛片app| 久中文字幕一区| 生活片a∨在线观看| 一区二区三区日韩欧美精品| 性欧美大战久久久久久久| 日韩三级影视| 欧美精品自拍偷拍| 中文字幕无人区二| 日本欧美高清| 色婷婷av一区二区三区久久| 欧美日韩成人免费观看| 亚洲综合三区| 91精品久久久久久久久久久| 男人天堂一区二区| 国产亚洲美州欧州综合国| av动漫免费观看| 超级白嫩亚洲国产第一| 在线免费观看一区| 深夜福利网站在线观看| 欧美在线关看| 精品国产区一区二区三区在线观看| 极品盗摄国产盗摄合集| 久久高清免费观看| 91探花福利精品国产自产在线| 天堂中文资源在线观看| 欧美国产日本视频| a级免费在线观看| 国产成人福利夜色影视| 亚洲国产精彩中文乱码av| 一区二区三区久久久久| 红桃视频欧美| 国产成人综合一区二区三区| 亚洲国产精品久久久久久久| 国产精品麻豆一区二区| 国产中文字幕视频在线观看| 国产精品中文| 一区二区三区久久精品| 日本三级黄色大片| 精品午夜一区二区三区在线观看| 久久久久网址| 啦啦啦中文在线观看日本| 欧美四级电影在线观看| 黄色性生活一级片| 欧美伊人影院| 国产精品自拍网| 国内在线精品| 欧美日韩国产精品一区二区三区四区 | 国产日韩亚洲精品| 成人免费高清| 欧美欧美午夜aⅴ在线观看| 成人h动漫精品一区| 精品白丝av| 91久久久一线二线三线品牌| av在线资源站| 在线视频你懂得一区| 少妇户外露出[11p]| 欧美精品18| 91亚洲永久免费精品| av在线电影免费观看| 色吊一区二区三区| 亚欧洲乱码视频| 麻豆91精品| 久久久久久一区| 超碰资源在线| 亚洲国产小视频| 日本熟妇毛耸耸xxxxxx| 成人午夜电影小说| www.欧美黄色| 国产一区丝袜| 91国自产精品中文字幕亚洲| 日本国产在线观看| 香蕉乱码成人久久天堂爱免费| 国产又粗又猛大又黄又爽| 亚洲第一天堂| 亚洲一区二区三区乱码aⅴ| 免费av在线网址| 欧美丰满美乳xxx高潮www| 97精品在线播放| 国内成人免费视频| 秋霞在线一区二区| 韩国三级大全久久网站| 欧美刺激性大交免费视频| 成人激情四射网| 亚洲电影一区二区| 免费成人蒂法网站| 亚洲欧美成人| 亚洲aⅴ天堂av在线电影软件| 欧美风情在线视频| 久久精品国产视频| 精品人妻aV中文字幕乱码色欲| 亚洲在线中文字幕| 色综合久久五月| 久久久久久穴| 一区二区不卡在线| 蜜桃精品一区二区三区| 久久免费精品日本久久中文字幕| 性高潮久久久久久久久久| 一本色道久久加勒比精品| 99自拍偷拍视频| 国产精品69久久久久水密桃| av在线播放亚洲| 精品国产精品| 91精品国产综合久久久久久丝袜| a级片免费在线观看| 亚洲乱码国产乱码精品精天堂| 艳妇乳肉豪妇荡乳av无码福利| 国产精品夫妻自拍| 成人欧美精品一区二区| 久久激情一区| 日韩人妻一区二区三区蜜桃视频| 精品网站aaa| 国产精品色婷婷视频| 青草在线视频| 国产一区二区三区欧美| 99久久久国产精品无码免费| 精品欧美国产一区二区三区| www.日本高清视频| 成人精品亚洲人成在线| 亚洲性生活网站| 午夜国产精品视频| 欧美一区二视频在线免费观看| 综合久久伊人| 日本精品视频在线观看| 尤物在线网址| 国产亚洲精品高潮| 欧美一级免费片| 欧美人牲a欧美精品| 精品无码久久久久久久久| 国产欧美综合在线| 国产人妻精品午夜福利免费| 日韩成人午夜精品| 国产情侣第一页| 四虎成人av| 欧美少妇一区| 风间由美一区二区av101| 国产欧美精品久久久| 天堂√中文最新版在线| 欧美老女人性视频| 日本在线视频网| 亚洲女人被黑人巨大进入al| 亚洲精品第五页| 欧美日韩的一区二区| 一级黄色av片| 精品久久香蕉国产线看观看gif| 欧美一区免费观看| 亚洲国产精品99久久久久久久久| 中文字幕人妻一区二区三区| 国产伦精品一区二区三区免费| 宅男噜噜噜66国产免费观看| 亚洲精品欧洲| a级免费在线观看| 亚洲天堂免费| 91麻豆天美传媒在线| 日韩免费av| 日韩精品不卡| 亚洲午夜久久| 久久综合一区| 无码日韩精品一区二区免费| 国产一区再线| 盗摄系列偷拍视频精品tp| 18成人在线| 日本在线视频一区二区三区| 成人日韩在线电影| 亚洲二区av| 国产一区香蕉久久| 欧洲亚洲精品| 成人在线观看视频网站| 亚洲精品aa| 成人免费网站在线观看| 9999精品| 91亚洲国产精品| 久久影院一区二区三区| 亚洲已满18点击进入在线看片| 久久三级中文| av一区二区三区在线观看| 亚洲一区二区三区日本久久九| 91精品国产一区二区三区动漫| 亚洲日本一区二区三区在线| av激情久久| 欧美一级二级三级视频| 蜜桃传媒视频麻豆第一区免费观看 | 欧美日韩精品中文字幕一区二区| 国产91精品对白在线播放| 日本一区视频在线观看| 精品免费视频| 中国一区二区三区| 中文字幕一区二区av| 欧美 亚洲 视频| 亚洲茄子视频| 久久久久国产精品熟女影院| 久久99精品国产麻豆婷婷洗澡| 成人亚洲免费视频| 国产馆精品极品| 中文字幕无码人妻少妇免费| 久久先锋影音av鲁色资源网| 国产又粗又猛又爽又黄av| 国产精品乱码人人做人人爱| 中文字幕av久久爽av| 污片在线观看一区二区| av手机天堂网| 日韩亚洲欧美一区二区三区| 少妇无码一区二区三区| 亚洲老头老太hd| 久久99精品久久| 91精品国产电影| jvid一区二区三区| 97视频中文字幕| 免费欧美视频| 蜜臀av性久久久久蜜臀av| 亚洲美女网站| 激情五月俺来也| 国产91在线看| av电影在线不卡| 一区二区三区在线视频观看58| 色播视频在线播放| 欧美日韩一二三区| 少妇精品高潮欲妇又嫩中文字幕 | 天堂8中文在线| 欧美一区二三区| 国产精品777777在线播放| 久久久水蜜桃| 午夜精品av| 东京热加勒比无码少妇| 国产乱妇无码大片在线观看| 亚洲成人网在线播放| 亚洲欧美激情一区二区| 日韩欧美三级视频| 3d动漫精品啪啪1区2区免费| 亚洲av成人精品一区二区三区在线播放 | 欧美日韩在线一二三| 在线国产一区二区| 成人精品视频一区二区| 成人黄色777网| 欧美手机在线观看| 在线亚洲精品福利网址导航| 天天av天天翘| 欧美美女操人视频| 国产福利亚洲| 久久精品人成| 狠狠爱综合网| 性久久久久久久久久久久久久| 久久久噜噜噜久久中文字幕色伊伊| 黄色在线观看免费| 欧美午夜寂寞影院| 日韩一二三四| 91av福利视频| 99国产精品免费网站| 中文字幕一区二区三区四区五区| 日韩二区三区在线观看| 亚洲av无码国产精品久久| 亚洲夂夂婷婷色拍ww47| 亚洲自拍偷拍另类| 国产亚洲精品久久久久久| xx欧美视频| 精品人伦一区二区三区| 午夜精品国产| 老司机av网站| 亚洲欧美色一区| 国产一区二区小视频| 在线日韩中文字幕| 香蕉成人影院| 日韩视频专区| 美日韩精品视频| 在线免费看黄视频| 欧美色播在线播放| 你懂的在线播放| 日韩美女福利视频| 九九久久成人| 青青青国产在线视频| 久久久综合精品| 成人h动漫精品一区二区下载| 亚洲另类欧美自拍| 视频在线日韩| 日韩精品另类天天更新| 日韩av电影天堂| 国产极品视频在线观看| 色视频欧美一区二区三区| 免费观看成年在线视频网站| 国产aaa精品| 日本一区二区三区视频| 15—17女人毛片| 自拍偷拍亚洲欧美日韩| 国产精品爽爽久久| 欧美巨猛xxxx猛交黑人97人| 粉嫩久久久久久久极品| 免费看一级大黄情大片| 26uuu久久天堂性欧美| 中文字幕一区在线播放| 国产亚洲精品美女久久久| 欧美成人毛片| 久久亚洲国产成人精品无码区 | 欧美一级专区免费大片| 免费看电影在线| 另类欧美小说| 蜜臀久久久99精品久久久久久| 欧美肥妇bbwbbw| 精品国产自在久精品国产| 性欧美又大又长又硬| 无码免费一区二区三区免费播放| 久久国产精品免费| 九九免费精品视频| 亚洲精品久久久久久久久| 日韩欧美一区二区三区免费观看| 在线免费观看成人| 懂色一区二区三区免费观看| 亚洲自拍一区在线观看| 久久久97精品| 老司机凹凸av亚洲导航| 国产高潮免费视频| 亚洲精品一卡二卡| 亚州男人的天堂| 成人午夜小视频| 99热精品在线| 国产又粗又长又黄的视频| 欧美成人激情免费网| 成人做爰视频www网站小优视频| 亚洲在线欧美| 99re视频精品| 一级特黄aaa大片在线观看| 欧美—级高清免费播放| 欧美一级本道电影免费专区| 不卡的一区二区| 色偷偷成人一区二区三区91 | 午夜视频免费看| 91精品中文在线| 蜜桃伊人久久| 久久这里只有精品免费| 日韩在线视频国产| 欧美深夜视频|