Bootstrap

SpringSecurity整合JWT+Oauth2认证

没写完,推荐下面的博客
推荐博客<查看pom依赖、数据库sql、实体类、数据映射>:SpringSecurity框架
推荐博客<查看SpringSecurity整合JWT+Oauth2认证>:SpringSecurity整合JWT+Oauth2认证

一 创建项目

测试浏览器:建议使用谷歌的无痕模式,不然浏览器有缓存。

1. 创建项目

输入名称:springbootJwt
在这里插入图片描述
选择依赖->创建
在这里插入图片描述
项目路径<不需要的文件可以删掉>
在这里插入图片描述
pom 文件依赖如下

<?xml version="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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!--父类2.7.4-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <!--项目说明-->
    <groupId>com.xxx</groupId>
    <artifactId>springbootJwt</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springbootJwt</name>
    <description>springbootJwt</description>
    <!--版本配置1.8-->
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <!--依赖-->
    <dependencies>
        <!--security-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--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>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!-- 集成MySQL连接 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <!--devtools-->
        <!--  <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-devtools</artifactId>
              <scope>runtime</scope>
              <optional>true</optional>
          </dependency>-->
        <!--processor-->
        <!--  <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-configuration-processor</artifactId>
              <optional>true</optional>
          </dependency>-->

        <!--test-->
        <!--  <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-test</artifactId>
              <scope>test</scope>
          </dependency>-->
        <!--security-test-->
        <!--   <dependency>
               <groupId>org.springframework.security</groupId>
               <artifactId>spring-security-test</artifactId>
               <scope>test</scope>
           </dependency>-->
        <!--hutool-all-->
        <!--  <dependency>
              <groupId>cn.hutool</groupId>
              <artifactId>hutool-all</artifactId>
              <version>5.7.17</version>
          </dependency>-->
        <!--commons-io-->
        <!--   <dependency>
               <groupId>commons-io</groupId>
               <artifactId>commons-io</artifactId>
               <version>2.11.0</version>
           </dependency>-->


    </dependencies>


</project>


2. 编写测试类

1. controller

package com.xxx.springbootjwt.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {
    @GetMapping("/slogan")
    public String slogan() {
        return "slogan ok !!!";
    }
}

2. 访问

访问:http://127.0.0.1:8080/slogan 自动跳转 http://127.0.0.1:8080/login

在这里插入图片描述

security 自带的登录页面
用户名: user 密码:控制台打印生成的密码

在这里插入图片描述
输入用户名,密码 点击Sign in
在这里插入图片描述

自动跳转到 http://127.0.0.1:8080/slogan
在这里插入图片描述

3. 配置文件设置账号密码

路径:src/main/resources/application.yml

spring:
  security:
    user:
      name: user
      password: 123456
      roles: manager

访问:http://127.0.0.1:8080/slogan 自动跳转 http://127.0.0.1:8080/login
输入用户名, 密码 点击sign in
在这里插入图片描述
自动跳转到 http://127.0.0.1:8080/slogan
在这里插入图片描述

4. UserDetailsService

1. 应用配置类

package com.xxx.springbootjwt.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * 应用配置类
 */
@Configuration
public class ApplicationConfig {

    @Bean
    public PasswordEncoder getPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

2. service 控制器

路径: src/main/java/com/xxx/springbootjwt/service/UserService.java

package com.xxx.springbootjwt.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;


/**
 *
 */
@Slf4j
@RequiredArgsConstructor //实现PasswordEncoder构造有参方法
@Service
public class UserService implements UserDetailsService {

    private final PasswordEncoder passwordEncoder;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("获取的账号 >>> {}", username);
        // 1. 数据库判断账号啊是否存在,不存在抛出异常
        if (!username.equals("user")) {
            throw new UsernameNotFoundException("用户不存在");
        }
        // 2. 根据账号查询到的密码(注册时候已经加密过)进行解析,或者直接把账号密码放入到构造方法
        String pwd = passwordEncoder.encode("123456");
        /*该用户拥有多个角色,每个角色用都好隔开*/
        List<GrantedAuthority> grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal");
        return new User(username, pwd, grantedAuthorities);
    }
}

3. 重启->登录,查看运行

在这里插入图片描述

5. 数据库设计

1. 数据库表sql

表名:security
在这里插入图片描述
表和数据sql:

/*
 Navicat Premium Data Transfer

 Source Server         : localhost
 Source Server Type    : MySQL
 Source Server Version : 80012
 Source Host           : localhost:3306
 Source Schema         : security

 Target Server Type    : MySQL
 Target Server Version : 80012
 File Encoding         : 65001

 Date: 11/10/2022 16:16:19
*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `rid` int(11) NOT NULL AUTO_INCREMENT,
  `role_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '名称',
  `role_name_zh` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '中文名称',
  PRIMARY KEY (`rid`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '权限表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'ROLE_dba', '数据库管理员');
INSERT INTO `role` VALUES (2, 'ROLE_admin', '系统管理员');
INSERT INTO `role` VALUES (3, 'ROLE_user', '用户');

-- ----------------------------
-- Table structure for sec_user
-- ----------------------------
DROP TABLE IF EXISTS `sec_user`;
CREATE TABLE `sec_user`  (
  `uid` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '',
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '',
  PRIMARY KEY (`uid`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sec_user
-- ----------------------------
INSERT INTO `sec_user` VALUES (1, 'root', '123456');
INSERT INTO `sec_user` VALUES (2, 'admin', '123456');
INSERT INTO `sec_user` VALUES (3, 'fj', '123456');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`  (
  `urid` int(11) NOT NULL AUTO_INCREMENT,
  `uid` int(11) NULL DEFAULT NULL,
  `rid` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`urid`) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户权限关联表' ROW_FORMAT = Fixed;

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

SET FOREIGN_KEY_CHECKS = 1;

2. ide链接数据库,生成pojo

  1. Database -> + -> Data Source -> mysql -> 输入数据库信息-> OK
    在这里插入图片描述

  2. 选择 生成pojo
    在这里插入图片描述

  3. 选择要存放的目录,点击ok
    在这里插入图片描述

  4. 查看生成目录
    在这里插入图片描述

3. 配置数据库链接

路径:src/main/resources/application.yml

spring:
  security:
    user:
      name: user
      password: 123456
      roles: manager
  datasource:
    url: jdbc:mysql://localhost:3306/security?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver



# mybatis配置
mybatis-plus:
  # xml文件路径
  mapper-locations: classpath:mapper/*.xml
  # 实体类路径
  type-aliases-package: com.xxx.springbootjwt.pojo
  configuration:
    # 驼峰转换
    map-underscore-to-camel-case: true
    # 是否开启缓存
    cache-enabled: false
    # 打印sql
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 全局配置
  global-config:
    # 数据库字段驼峰下划线转换
    db-column-underline: true
    # id自增类型(数据库id自增)
    id-type: 0

4. 修改实体类

  1. 修改sec_user
package com.xxx.springbootjwt.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@TableName("sec_user")
public class SecUser extends Model<SecUser> {

    private static final long serialVersionUID = -2109089699649055035L;
    @TableId(value = "uid",type = IdType.AUTO)
    private Long uid;
    @TableField("username")
    private String username;
    @TableField("password")
    private String password;


}

  1. 修改role
package com.xxx.springbootjwt.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@TableName("role")
public class Role extends Model<Role> {
    private static final long serialVersionUID = -2109089699649055035L;
    @TableId(value = "rid", type = IdType.AUTO)
    private Long rid;
    @TableField("role_name")
    private String roleName;
    @TableField("role_name_zh")
    private String roleNameZh;


}

  1. 修改user_role
package com.xxx.springbootjwt.pojo;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@TableName("user_role")
public class UserRole extends Model<UserRole> {
  private static final long serialVersionUID = -2109089699649055035L;
  @TableId(value = "urid", type = IdType.AUTO)
  private Long urid;
  @TableField("uid")
  private Long uid;
  @TableField("rid")
  private Long rid;


}

5. 添加mapper接口类

  1. SecUserMapper
package com.xxx.springbootjwt.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xxx.springbootjwt.pojo.SecUser;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface SecUserMapper extends BaseMapper<SecUser> {
    /**
     *用户id虎丘该用户的角色列表
     *
     */
    List<SecUser> selectSecUserByUid(@Param("uid") Long uid);
}

  1. RoleMapper
package com.xxx.springbootjwt.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xxx.springbootjwt.pojo.Role;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface RoleMapper extends BaseMapper<Role> {
    /**
     *用户id虎丘该用户的角色列表
     *
     */
   List<Role> selectRoleByUid(@Param("uid") Long uid);
}

  1. UserRoleMapper
package com.xxx.springbootjwt.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xxx.springbootjwt.pojo.UserRole;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

@Mapper
public interface UserRoleMapper extends BaseMapper<UserRole> {

    /**
     *用户id虎丘该用户的角色列表
     *
     */
    List<UserRole> selectUserRoleByUid(@Param("uid") Long uid);
}

6. 添加mapper.xml

  1. SecUserMapper.xml

路径:src/main/resources/mapper/SecUserMapper.xml

<?xml version="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.xxx.springbootjwt.mapper.SecUserMapper">
<select id="selectSecUserByUid" resultType="com.xxx.springbootjwt.pojo.SecUser" parameterType="java.lang.Long">
    select * from sec_user where uid =#{uid}
</select>

</mapper>

  1. RoleMapper.xml

路径:src/main/resources/mapper/RoleMapper.xml

<?xml version="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.xxx.springbootjwt.mapper.RoleMapper">
    <select id="selectRoleByUid" resultType="com.xxx.springbootjwt.pojo.Role" parameterType="java.lang.Long">
        select r.* from role r inner join user_role ur where ur.uid =#{uid} and r.rid = ur.rid
    </select>

</mapper>

  1. UserRoleMapper.xml

路径:src/main/resources/mapper/UserRoleMapper.xml

<?xml version="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.xxx.springbootjwt.mapper.UserRoleMapper">
    <select id="selectUserRoleByUid" resultType="com.xxx.springbootjwt.pojo.UserRole" parameterType="java.lang.Long">
        select * from user_role where uid =#{uid}
    </select>

</mapper>

7. 修改service控制器

路径: src/main/java/com/xxx/springbootjwt/service/UserService.java

package com.xxx.springbootjwt.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.xxx.springbootjwt.mapper.RoleMapper;
import com.xxx.springbootjwt.mapper.SecUserMapper;
import com.xxx.springbootjwt.pojo.Role;
import com.xxx.springbootjwt.pojo.SecUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.stream.Collectors;


/**
 * UserDetailsService实体类
 */
@Slf4j
@RequiredArgsConstructor //实现PasswordEncoder构造有参方法
@Service
public class UserService implements UserDetailsService {

    private final PasswordEncoder passwordEncoder;
    private final SecUserMapper secUserMapper;
    private final RoleMapper roleMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        log.info("获取的账号 >>> {}", username);
        // 1. 数据库判断账号啊是否存在,不存在抛出异常
       /* if (!username.equals("user")) {
            throw new UsernameNotFoundException("用户不存在");
        }*/
        // 2. 根据账号查询到的密码(注册时候已经加密过)进行解析,或者直接把账号密码放入到构造方法

        QueryWrapper<SecUser> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        SecUser secUser = secUserMapper.selectOne(wrapper);
        if (secUser == null) {
            throw new UsernameNotFoundException("账号不存在");
        }
        String pwd = passwordEncoder.encode(secUser.getPassword());
        //根据用户id获取该用户的角色列表
        List<Role> roles = roleMapper.selectRoleByUid(secUser.getUid());
        if (roles == null) {
            //返回空的列表
            return new User(username, pwd, AuthorityUtils.createAuthorityList());
        }

        /*该用户拥有多个角色,每个角色用都好隔开*/
        //List<GrantedAuthority> grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal");
        //Collection<? extends GrantedAuthority> roleNames;
        List<SimpleGrantedAuthority> collect = roles.stream().map(role -> new SimpleGrantedAuthority(role.getRoleName())).collect(Collectors.toList());
        return new User(username, pwd, collect);
    }
}

8. 测试

访问:http://127.0.0.1:8080/slogan 跳转登录
在这里插入图片描述
跳转成功
在这里插入图片描述
查看控制台
在这里插入图片描述

;