第3节-Security用户管理之数据库用户管理

开场白

这一节我们来说说Security是如何通过数据库来对用户进行鉴权的,就以目前比较流行的MyBatis+MySQL为例。当然,如果你的ORM层是用JPA或数据库是用Oracle基本也都类似。我这里所用到的MyBatis也是以最简单的方式来使用,因为毕竟这不是一篇讲MyBatis的教程。下面开始啦~如果没有看过前2节的同学可以先翻阅一下前2节的内容:
第1节-SpringBoot整合Security
第2节-Security用户管理之内存用户管理

1、新增Jar包

在POM文件的【properties】中加入如下信息:

<mybatis.version>3.4.6</mybatis.version>
<mybatis-spring.version>1.3.2</mybatis-spring.version>

在POM文件的【dependencies】中加入如下信息:

<!--    mysql                                                         -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!--    mybatis     ORM层                                             -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>${mybatis.version}</version>
</dependency>

<!--    mybatis     整合SpringBoot                                    -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>${mybatis-spring.version}</version>
</dependency>

<!--    lombok      简化POJO代码,省去get,set方法,并可生成toString等方法    -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

这4个包分别是:
(1)、mysql链接包
(2)、mybatis包
(3)、mybatis与spring-boot整合包
(4)、lombok包,自动生成POJO中的get、set、toString方法,简化日志操作。

2、修改配置文件

修改【src\main\resources】下的配置文件【application.properties】内容:

# mysql
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/mysecurity5?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456

# 开启Mybatis下划线命名转驼峰命名
mybatis.configuration.map-underscore-to-camel-case=true

修改后,启动项目,发现项目可以正常启动。
如果未配置数据库信息的话可能会提示如下错误:


未配置数据库信息

3、创建数据表并写入测试数据

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` char(36) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键,唯一标识',
  `loginName` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '登录名',
  `loginPwd` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '登录密码',
  `nickName` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户昵称',
  `role` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES ('1', 'LeoLee', '$2a$10$uDQWkXJHI6gEURvnZmpareZYB53Xbe4p6twWITQU/PYZWu95wEI7O', 'Leo', 'USER');
INSERT INTO `sys_user` VALUES ('2', 'KimQu', '$2a$10$S8De83OCGxmVX3Q8nPs.Ou3Pfl6ovzVJrRVAVAXWU6xaRZRopt/qq', 'Kim', 'ADMIN');

4、创建Model、DAO、Service

(1)创建UserModel

@Data
@Alias("userModel")
public class UserModel implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 主键,唯一标识
     */
    private String id;

    /**
     * 登录名
     */
    private String loginName;

    /**
     * 登录密码
     */
    private String loginPwd;

    /**
     * 昵称
     */
    private String nickName;

    /**
     * 用户角色
     */
    private String role;
}

PS:为了演示方便,就直接把role丢到user表里吧,在下一节整理代码的时候再把role提出来形成表,然后通过中间表进行映射。
(2)创建UserDAO

@Mapper
@Repository
public interface UserDAO {

    @Select("SELECT * FROM sys_user WHERE id = #{id}")
    public UserModel load(String id);

    @Select("SELECT * FROM sys_user WHERE loginName = #{loginName}")
    public UserModel loadByLoginName(String loginName);
}

PS:暂且先把查询语句丢到上面吧,下一节整理代码的时候再将语句放入XML中。
(3)创建UserService接口

public interface UserService {

    /**
     * 通过主键加载用户
     * @param id 主键,唯一标识
     * @return 用户对象
     */
    public UserModel load(String id);

    /**
     * 通过登录名加载用户
     * @param loginName 登录名
     * @return
     */
    public UserModel loadByLoginName(String loginName);
}

PS:暂且……你懂的,下一节整理代码的时候封装一下……
(4)创建UserServiceImpl实现类

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDAO userDAO;

    @Override
    public UserModel load(String id) {
        return userDAO.load(id);
    }

    @Override
    public UserModel loadByLoginName(String loginName) {
        return userDAO.loadByLoginName(loginName);
    }
}

PS:高兴的话可以写个测试类,不高兴的话就这么着吧……,目测应该可以过……除非哪里出了错……

5、创建及修改Security所需要的类

(1)创建UserDetailsServiceImpl类

@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String loginName) throws UsernameNotFoundException {

        // 从数据库加载用户信息
        UserModel userModel = userService.loadByLoginName(loginName);

        // 判断用户是否存在
        if( null == userModel ) {
            throw new UsernameNotFoundException("用户不存在,请确认后重试~");
        }

        //  用户角色列表其实就是权限……
        Collection<GrantedAuthority> authorities = new ArrayList<>();

        authorities.add(new SimpleGrantedAuthority(userModel.getRole() ) );

        // 返回UserDetails实现类
        return new User(userModel.getLoginName(), userModel.getLoginPwd(), authorities);
    }
}

PS:Security要求用户类要实现UserDetailsService接口,其实……其实……也可以直接在UserServiceImpl上进行实现,只是感觉那样偶合性有些高……
(2)修改SecurityConfig配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
//        .withUser("LeoLee").password( "$2a$10$uDQWkXJHI6gEURvnZmpareZYB53Xbe4p6twWITQU/PYZWu95wEI7O").roles("USER")
//        .and()
//        .withUser("KimQu").password( "$2a$10$S8De83OCGxmVX3Q8nPs.Ou3Pfl6ovzVJrRVAVAXWU6xaRZRopt/qq").roles("ADMIN");
        auth.userDetailsService(userDetailsService).passwordEncoder( new BCryptPasswordEncoder() );
    }
}

PS:将原来的内存验证注释上,修改为通过userDetailsService进行验证,然后加密方法为BCryptPasswordEncoder

这一节就说到这里,我们先来回顾一下这一节的项目结构

第3节 项目结构图

与往节一样,图中红色框框中的是这一节新增或修改的部分,请大家自己对照~

6、运行项目

进入登录页面,输入【LeoLee】与【123456】进行登录~


用户登录前
用户登录后

迷惘不? 彷徨不?惆怅不?抑郁不?迷迷糊糊莫名妙就好用了……。你的方法里甚至只是通过用户名加载了一下用户,然后把用户信息返回去都没有验证就完事儿了

莫慌,下一节我们通过前三节的基础来为大家讲解一下Security的运作原理、BCryptPasswordEncoder加密方式顺路整理一下前几节的代码。

请看下节:04-Security入门

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容