日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁(yè) > 科技  > 軟件

Spring Boot Security + JWT Token 的簡(jiǎn)單應(yīng)用

來(lái)源: 責(zé)編: 時(shí)間:2023-12-09 15:23:38 350觀看
導(dǎo)讀今天主要介紹以下內(nèi)容:用戶可以注冊(cè)新帳戶,或使用用戶名和密碼登錄。根據(jù)用戶的權(quán)限,我們授權(quán)用戶訪問(wèn)資源今日內(nèi)容介紹,大約花費(fèi)40分鐘圖片1.Spring Boot 注冊(cè)和登錄with JWT 身份驗(yàn)證流程下圖顯示了我們?nèi)绾螌?shí)現(xiàn)用戶注

今天主要介紹以下內(nèi)容:F6u28資訊網(wǎng)——每日最新資訊28at.com

  • 用戶可以注冊(cè)新帳戶,或使用用戶名和密碼登錄。
  • 根據(jù)用戶的權(quán)限,我們授權(quán)用戶訪問(wèn)資源

今日內(nèi)容介紹,大約花費(fèi)40分鐘F6u28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

1.Spring Boot 注冊(cè)和登錄with JWT 身份驗(yàn)證流程

下圖顯示了我們?nèi)绾螌?shí)現(xiàn)用戶注冊(cè)、用戶登錄和授權(quán)流程的流程。F6u28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

如果客戶端訪問(wèn)受保護(hù)的資源,則必須將合法的 JWT 添加到 HTTP 授權(quán)標(biāo)頭中。F6u28資訊網(wǎng)——每日最新資訊28at.com

Spring Boot中使用Spring Security

您可以通過(guò)下圖概述我們的Spring Boot項(xiàng)目:F6u28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

Spring Security介紹:F6u28資訊網(wǎng)——每日最新資訊28at.com

  • WebSecurityConfig: spring Security 配置類,用于配置 Spring Security 的行為和規(guī)則。它為受保護(hù)的資源配置 cors、csrf、會(huì)話管理、規(guī)則。我們還可以擴(kuò)展和自定義包含以下元素的默認(rèn)配置。這個(gè)類通常擴(kuò)展自 WebSecurityConfigurerAdapter

注意:WebSecurityConfigurerAdapter 從 SpringBoot 2.7.0 開(kāi)始被棄用)F6u28資訊網(wǎng)——每日最新資訊28at.com

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

2.項(xiàng)目準(zhǔn)備

下圖是Spring Boot項(xiàng)目的文件夾和文件結(jié)構(gòu):F6u28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

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

2.1. 創(chuàng)建表

根據(jù)Sql創(chuàng)建表,表間關(guān)系如下:F6u28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

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 '權(quán)限名稱',                                 `code` varchar(20) DEFAULT NULL,                                 `description` text COMMENT '權(quán)限描述',                                 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 '權(quán)限名稱',                           `description` varchar(255) DEFAULT NULL COMMENT '說(shuō)明',                           PRIMARY KEY (`id`),                           UNIQUE KEY `UK_k3beff7qglfn58qsf2yvbg41i` (`name`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;-- ------------------------------ Records of pe_role-- ----------------------------INSERT INTO `pe_role` VALUES ('1', '系統(tǒng)管理員', '系統(tǒng)日常維護(hù)');INSERT INTO `pe_role` VALUES ('2', '普通員工', '普通操作權(quán)限');-- ------------------------------ 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 '角色I(xiàn)D',                                      `permission_id` varchar(40) NOT NULL COMMENT '權(quán)限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 '角色I(xiàn)D',                                `user_id` varchar(40) NOT NULL COMMENT '權(quán)限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插件,這里就不過(guò)多介紹如何安裝插件了,和其他插件安裝相同 表生成代碼步驟如下:F6u28資訊網(wǎng)——每日最新資訊28at.com

  • 選擇表右鍵選擇MybatisX-Generator

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

  • 選擇代碼生成位置

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

  • 選擇生成代碼的規(guī)則

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

  • 我們根據(jù)規(guī)則將以下表進(jìn)行生成:
  • pe_permission
  • pe_role
  • pe_role_permission
  • pe_user
  • pe_user_role

2.4. 創(chuàng)建UserDetailsService實(shí)現(xiàn)類

Spring Security 將加載用戶詳細(xì)信息以執(zhí)行身份驗(yàn)證和授權(quán)。所以它有 UserDetailsService 我們需要實(shí)現(xiàn)的接口。F6u28資訊網(wǎng)——每日最新資訊28at.com

在com.zbbmeta.service.impl包下創(chuàng)建UserDetailsService實(shí)現(xiàn)類UserDetailsServiceImplF6u28資訊網(wǎng)——每日最新資訊28at.com

@Servicepublic 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類中添加查詢用戶和查詢權(quán)限方法

  • 在UserMapper添加根據(jù)用戶名查詢用戶方法
public interface UserMapper extends BaseMapper<User> {    User findByUsername(String name);}
  • 在UserMapper.xml添加方法的實(shí)現(xiàn)

在User實(shí)體類中添加字段rolesF6u28資訊網(wǎng)——每日最新資訊28at.com

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

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

并且在resultMap添加一對(duì)多根據(jù)用戶查詢對(duì)應(yīng)角色F6u28資訊網(wǎng)——每日最新資訊28at.com

<?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"/>        <!--一對(duì)多映射用這個(gè)  ofTyp是一對(duì)多的集合的所存放的實(shí)體類  javaType實(shí)體類的屬性類型-->        <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添加根據(jù)id查詢角色
public interface RoleMapper extends BaseMapper<Role> {    List<Role> queryRoleListByUserId(@Param("id") Long id);}
  • 在RoleMapper.xml添加方法的實(shí)現(xiàn) 在Role實(shí)體類中添加字段permissions
@TableField(exist = false)    private List<Permission> permissions = new ArrayList<>();//用戶與角色   多對(duì)多

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

并且在resultMap添加一對(duì)多根據(jù)角色查詢對(duì)應(yīng)權(quán)限F6u28資訊網(wǎng)——每日最新資訊28at.com

<?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添加根據(jù)id查詢權(quán)限
public interface PermissionMapper extends BaseMapper<Permission> {    List<Permission> queryPermissionList(@Param("id") Long id);}
  • 在PermissionMapper.xml添加方法的實(shí)現(xiàn)
<?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,因?yàn)閃ebSecurityConfigurerAdapter 從 Spring 2.7.0 中棄用F6u28資訊網(wǎng)——每日最新資訊28at.com

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

jwt:  config:    key: zbbmeta    ttl: 3600

【步驟一】: 創(chuàng)建JwtUtil工具類

在com.zbbmeta.util包下創(chuàng)建JwtUtil類F6u28資訊網(wǎng)——每日最新資訊28at.com

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")@Datapublic 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;    }}

【步驟二】:創(chuàng)建 WebSecurityConfig 類

WebSecurityConfig類 是我們安全認(rèn)證的關(guān)鍵。它為受保護(hù)的資源配置 cors、csrf、會(huì)話管理、規(guī)則。F6u28資訊網(wǎng)——每日最新資訊28at.com

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@EnableWebSecuritypublic 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;    }    /**     * 認(rèn)證     * @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()//不需要通過(guò)登錄驗(yàn)證就可以被訪問(wèn)的資源路徑                .anyRequest().authenticated();        http.authenticationProvider(authenticationProvider());        http.addFilterBefore(authTokenFilter(), UsernamePasswordAuthenticationFilter.class);        return http.build();    }}

注: 同樣的在 Spring Security 5.8 后要把  antMatchers() 改為 requestMatchers()F6u28資訊網(wǎng)——每日最新資訊28at.com

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

【步驟三】:創(chuàng)建過(guò)濾器篩選請(qǐng)求

在 com.zbbmeta.filter包下創(chuàng)建AuthTokenFilter類,對(duì)每個(gè)請(qǐng)求進(jìn)行過(guò)濾。 AuthTokenFilter 集成 OncePerRequestFilter 和覆蓋 doFilterInternal() 方法的類。F6u28資訊網(wǎng)——每日最新資訊28at.com

@Slf4jpublic 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();        //登錄或者注冊(cè)放行        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);    }}

我們?cè)诶锩?doFilterInternal() 做什么:F6u28資訊網(wǎng)——每日最新資訊28at.com

  • 從 Authorization 標(biāo)頭獲取 JWT (通過(guò)刪除 Bearer 前綴)
  • – 如果請(qǐng)求有 JWT ,請(qǐng)驗(yàn)證它, 將username 從token中解析

【步驟四】:處理身份驗(yàn)證異常

創(chuàng)建 AuthEntryPointJwt 實(shí)現(xiàn)接口的 AuthenticationEntryPoint 類。然后我們重寫(xiě)該 commence() 方法。每當(dāng)未經(jīng)身份驗(yàn)證的用戶請(qǐng)求受保護(hù)的 HTTP 資源并 AuthenticationException 拋出時(shí),都會(huì)觸發(fā)此方法。F6u28資訊網(wǎng)——每日最新資訊28at.com

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@Componentpublic 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);    }}

【步驟五】:創(chuàng)建Controller進(jìn)行登錄和注冊(cè)

  • /api/auth/signup: 注冊(cè)用戶
  • 檢查現(xiàn)有 username
  • 新建 User
  • 保存 User 到數(shù)據(jù)庫(kù)
  • /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")@RestControllerpublic 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) {        //根據(jù)用戶名查找用戶        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);    }    /**     * 用戶注冊(cè)     * @param signupDto     * @return     */    @PostMapping("/signup")    public Result registerUser( @RequestBody SignupDto signupDto){        //根據(jù)用戶名獲取用戶        User user = userService.findByName(signupDto.getUsername());        //用戶不是null表示用戶已經(jīng)存在        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<>();        //如果沒(méi)有用戶角色,默認(rèn)添加普通員工        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);        }        //添加用戶和角色關(guān)系        userRoleService.saveBatch(userRolesList);        return Result.SUCCESS("注冊(cè)成功!");    }}

4.測(cè)試

  • 使用PostMan進(jìn)行用戶注冊(cè)

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

注冊(cè)后的pe_user表格數(shù)據(jù)如下所示:F6u28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

  • 訪問(wèn)受保護(hù)的資源:GET /api/tutorials/published

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

因?yàn)槲覀儧](méi)有登錄,所以受保護(hù)的資源不能訪問(wèn)F6u28資訊網(wǎng)——每日最新資訊28at.com

  • 登錄賬號(hào):POST /api/auth/signin

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

  • 復(fù)制Token:重新訪問(wèn)受保護(hù)資源

圖片圖片F6u28資訊網(wǎng)——每日最新資訊28at.com

今天,我們?cè)赟pring Boot示例中學(xué)到關(guān)于Spring Security和基于JWT令牌的身份驗(yàn)證的有趣知識(shí)。盡管我們寫(xiě)了很多代碼,但我希望你能理解應(yīng)用程序的整體架構(gòu),并輕松地將其應(yīng)用到你的項(xiàng)目中。F6u28資訊網(wǎng)——每日最新資訊28at.com

## 代碼地址https://github.com/bangbangzhou/spring-boot-backend-example.git

本文鏈接:http://www.www897cc.com/showinfo-26-40684-0.htmlSpring Boot Security + JWT Token 的簡(jiǎn)單應(yīng)用

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: MySQL數(shù)據(jù)庫(kù)壓力測(cè)試與性能評(píng)估方法:Java實(shí)戰(zhàn)

下一篇: Java開(kāi)發(fā)者的MySQL數(shù)據(jù)庫(kù)版本管理策略

標(biāo)簽:
  • 熱門(mén)焦點(diǎn)
Top 主站蜘蛛池模板: 琼结县| 周宁县| 弋阳县| 综艺| 九江市| 通辽市| 沾化县| 蓬溪县| 开化县| 浦东新区| 右玉县| 西安市| 阿勒泰市| 长葛市| 扶绥县| 鹿邑县| 荔浦县| 开鲁县| 万载县| 永善县| 固阳县| 泉州市| 涞源县| 枣阳市| 阳西县| 乐至县| 合阳县| 三门县| 廊坊市| 金门县| 衡水市| 内丘县| 宜川县| 西林县| 龙海市| 子洲县| 全南县| 武安市| 霍州市| 岗巴县| 泰兴市|