一篇文章带你搞定 springsecurity基于数据库的认证(springsecurity整合mybatis)

一、前期配置

1. 加入依赖

<dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.1.10</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
      <version>5.1.27</version>
    </dependency>

这里选定的 mysql-connector-java 连接版本是 5.1.27,对应的 application.properties 为:

spring.datasource.url=jdbc:mysql://localhost:3306/yolo
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.username=root
spring.datasource.password=root

如果是 8.0以上版本则 application.properties 需要对于 spring.datasource.url 需要加入 serverTimezone

spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai

2. 数据库脚本

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(32) DEFAULT NULL,
 `nameZh` varchar(32) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

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

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(32) DEFAULT NULL,
 `password` varchar(255) DEFAULT NULL,
 `enabled` tinyint(1) DEFAULT NULL,
 `locked` tinyint(1) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'root', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');
INSERT INTO `user` VALUES ('2', 'admin', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');
INSERT INTO `user` VALUES ('3', 'sang', '$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq', '1', '0');

-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `uid` int(11) DEFAULT NULL,
 `rid` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

-- ----------------------------
-- 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', '2', '2');
INSERT INTO `user_role` VALUES ('4', '3', '3');
SET FOREIGN_KEY_CHECKS=1;

二、定义实体类

1. 定义 User

实体类 User 需要实现 UserDetails 接口,因为每个人都可以定义 User 对象,但是每个人定义的 User对象不一样,这就造成每个人设置的属性不一样,当系统需要判定用户的登录状态时,因为用户名和密码的名称设置的五花八门,造成无法确定调用哪个

所以这里要求所有的实体类实现 UserDetails 接口,它就相当于一个规范定义了登录验证时需要的属性名称,所以的实体类都要符合这个规范。

public class User implements UserDetails {
  private Integer id;
  private String username;
  private String password;
  private Boolean enabled;
  private Boolean locked;
  private List<Role> roles;
  public List<Role> getRoles() {
    return roles;
  }
  public void setRoles(List<Role> roles) {
    this.roles = roles;
  }
  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  @Override
  public String getUsername() {
    return username;
  }
  @Override
  public boolean isAccountNonExpired() {
    return true;
  }
  @Override
  public boolean isAccountNonLocked() {
    return !locked;
  }
  @Override
  public boolean isCredentialsNonExpired() {
    return true;
  }
  @Override
  public boolean isEnabled() {
    return enabled;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    List<SimpleGrantedAuthority> authorities = new ArrayList<>();
    for (Role role : roles) {
      authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
    }
    return authorities;
  }
  @Override
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  public void setEnabled(Boolean enabled) {
    this.enabled = enabled;
  }
  public void setLocked(Boolean locked) {
    this.locked = locked;
  }
}

(1) accountNonExpired、accountNonLocked、credentialsNonExpired、enabled 这四个属性分别用来描述用户的状态,表示账户是否没有过期、账户是否没有被锁定、密码是否没有过期、以及账户是否可用。
(2)roles 属性表示用户的角色,User 和 Role 是多对多关系,用一个 @ManyToMany 注解来描述。
(3)getAuthorities 方法返回用户的角色信息,我们在这个方法中把自己的 Role 稍微转化一下即可。

(1)这个集合是返回用户的所有角色,因为从数据库得到是 roles,但是需要的是一个集合形式的 getAuthorities,所以需要对其进行处理。

 @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    List<SimpleGrantedAuthority> authorities = new ArrayList<>();
    for (Role role : roles) {
      authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));
    }
    return authorities;
  }

这里注意springsecurity 角色的认证有一个要求,必须是以 ROLE_ 开始的,否则会出现问题:

authorities.add(new SimpleGrantedAuthority("ROLE_"+role.getName()));

当然也可以在数据中添加用户时,就让用户以 ROLE_ 开始,这样就不用二次添加了

(2)另外需要注意这里的 isAccountNonLocked(),账户是否没有被锁定,数据库中存储的是:

所以这里是对 locked 取的反

@Override
  public boolean isAccountNonLocked() {
    return !locked;
  }

2. 定义 Role

public class Role {
  private Integer id;
  private String name;
  private String nameZh;

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getNameZh() {
    return nameZh;
  }

  public void setNameZh(String nameZh) {
    this.nameZh = nameZh;
  }
}

三、定义 Service

@Service
public class UserService implements UserDetailsService {
  @Autowired
  UserMapper userMapper;

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userMapper.loadUserByUsername(username);
    if (user == null) {
      throw new UsernameNotFoundException("用户不存在!");
    }
    user.setRoles(userMapper.getUserRolesById(user.getId()));
    return user;
  }
}

我们自己定义的 UserService 需要实现 UserDetailsService 接口,实现该接口,就要实现接口中的方法,也就是 loadUserByUsername ,这个方法的参数就是用户在登录的时候传入的用户名,根据用户名去查询用户信息(查出来之后,系统会自动进行密码比对)。

四、定义 Mapper

1. UserMapper

@Mapper
public interface UserMapper {
  User loadUserByUsername(String username);

  List<Role> getUserRolesById(Integer id);
}

2. UserMapper.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="org.javaboy.securitydb.mapper.UserMapper">
  <select id="loadUserByUsername" resultType="org.yolo.securitymybatis.bean.User">
    select * from user where username=#{username}
  </select>

  <select id="getUserRolesById" resultType="org.yolo.securitymybatis.bean.Role">
    select * from role where id in (select rid from user_role where uid=#{id})
  </select>
</mapper>

Mapper 的位置:

所以需要为其添加资源路径:资源配置路径添加到 pom 文件中

<resources>
      <resource>
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </resource>
      <resource>
        <directory>src/main/resources</directory>
      </resource>
</resources>

五、定义 SecurityConfig

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  @Autowired
  UserService userService;

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userService);
  }

  @Bean
  PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }
  @Bean
  RoleHierarchy roleHierarchy() {
    RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
    String hierarchy = "ROLE_dba > ROLE_admin \n ROLE_admin > ROLE_user";
    roleHierarchy.setHierarchy(hierarchy);
    return roleHierarchy;
  }
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/dba/**").hasRole("dba")
        .antMatchers("/admin/**").hasRole("admin")
        .antMatchers("/user/**").hasRole("user")
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .permitAll()
        .and()
        .csrf().disable();
  }
}

测试访问:成功

到此这篇关于一篇文章带你搞定 springsecurity基于数据库的认证(springsecurity整合mybatis)的文章就介绍到这了,更多相关springsecurity整合mybatis内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JavaWeb开发之Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架

    简单介绍一下,本框架的基本功能点: Spring:整个框架的主体部分,这个自不用说. SpringMVC:MVC部分我还是比较喜欢Spring的. MyBatis:选型的时候选择这个ORM主要也是考虑其灵活性的问题,毕竟我也不知道,今后会遇到怎样的需求,用Hibernate一来是不太会用,二来,我还是比较喜欢直接写SQL来的简单一点. SpringSecurity:这个主要是安全框架,负责用户登录验证及整站权限分配的相关事项(权限分配真的很有用,这个我就不多说了). EhCache:一个非常流行

  • 一篇文章带你搞定 springsecurity基于数据库的认证(springsecurity整合mybatis)

    一.前期配置 1. 加入依赖 <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>mysql</groupId> &

  • 一篇文章带你搞定SpringBoot中的热部署devtools方法

    一.前期配置 创建项目时,需要加入 DevTools 依赖 二.测试使用 (1)建立 HelloController @RestController public class HelloController { @GetMapping("/hello") public String hello(){ return "hello devtools"; } } 对其进行修改:然后不用重新运行,重新构建即可:只加载变化的类 三.热部署的原理 Spring Boot 中热部

  • 一篇文章带你搞定SpringBoot不重启项目实现修改静态资源

    一.通过配置文件控制静态资源的热部署 在配置文件 application.properties 中添加: #表示从这个默认不触发重启的目录中除去static目录 spring.devtools.restart.exclude=classpath:/static/** 或者使用: #表示将static目录加入到修改资源会重启的目录中来 spring.devtools.restart.additional-paths=src/main/resource/static 此时对static 目录下的静态

  • 一篇文章带你搞定Ubuntu中打开Pycharm总是卡顿崩溃

    由于 Ubuntu 中的汉字输入实在是太不友好了,所以装了个 搜狗输入法,好不容易把 搜狗输入法装好,本以为可以开开心心的搞代码了,然而... pycharm 一打开,就崩溃,关不掉,进程杀死还是不行,只能关机重启. 本以为 pycharm 出现了问题,又重装了两遍,还是不行. 最终发现竟然是搜狗输入法以及 fcitx 输入法的锅 唉,只能老老实实的把 fctix 和搜狗输入法卸载了: (1)Ubuntu 软件里卸载 fctix,然后将键盘输入法系统改成 IBus (2)卸载搜狗输入法 先查找软

  • 一篇文章带你搞定Python多进程

    目录 1.Python多进程模块 2.Python多进程实现方法一 3.Python多进程实现方法二 4.Python多线程的通信 5.进程池 1.Python多进程模块 Python中的多进程是通过multiprocessing包来实现的,和多线程的threading.Thread差不多,它可以利用multiprocessing.Process对象来创建一个进程对象.这个进程对象的方法和线程对象的方法差不多也有start(), run(), join()等方法,其中有一个方法不同Thread线

  • 一篇文章带你搞定JAVA反射

    目录 1.反射的概念 1.概念 2.获取字节码文件对象的方式 2.1 元数据的概念 2.2 获取class对象的方式 1.访问权限 2.获取方法 2.1 访问静态方法 2.2 访问类方法 3.获取字段,读取字段的值 4.获取实现的接口 5.获取构造函数,创建实例 6.获取继承的父类 7.获取注解 4.反射实例 5.总结 1.反射的概念 1.概念 反射,指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对任意一个对象,都能调用它的任意一个方法.这种动态获取信息,以及动态调用对象方法

  • 一篇文章带你搞定JAVA内存泄漏

    目录 1.什么是内存泄漏 2.内存泄漏的原因 3.内存泄漏有哪些情况 3.1 代码中没有及时释放,导致内存无法回收. 3.2 资源未关闭造成的内存泄漏 3.3 全局缓存持有的对象不使用的时候没有及时移除,导致一直在内存中无法移除 3.4 静态集合类 3.5 堆外内存无法回收 4.内存泄漏的解决办法 5.内存问题排查 第一步 首先确认逻辑问题 第二步:分析gc是否正常执行 第三步 确认下版本新增代码的改动,尽快从代码上找出问题. 第四步:开启各种命令行和 导出 dump 各种工具分析 总结: 1.

  • 一篇文章带你搞定JAVA泛型

    目录 1.泛型的概念 2.泛型的使用 3.泛型原理,泛型擦除 3.1 IDEA 查看字节码 3.2 泛型擦除原理 4.?和 T 的区别 5.super extends 6.注意点 1.静态方法无法访问类的泛型 2.创建之后无法修改类型 3.类型判断问题 4.创建类型实例 7.总结 1.泛型的概念 泛型的作用就是把类型参数化,也就是我们常说的类型参数 平时我们接触的普通方法的参数,比如public void fun(String s):参数的类型是String,是固定的 现在泛型的作用就是再将St

  • 一篇文章带你搞定JAVA注解

    目录 1.注解是什么 2.jdk支持的注解有哪些 2.1 三种常用的注解: 2.2 元注解 3.注解实例 1.自定义注解 2.在对应的方法上增加注解 3.在项目启动的时候检查注解的枚举 4.总结 1.注解是什么 Java 注解用于为 Java 代码提供元数据,看完这句话也许你还是一脸懵逼,用人话说就是注解不直接影响你的代码执行,仅提供信息.接下我将从注解的定义.元注解.注解属性.自定义注解.注解解析JDK 提供的注解这几个方面再次了解注解(Annotation) 2.jdk支持的注解有哪些 2.

  • 一篇文章带你搞定JAVA Maven

    目录 1.maven是什么,为什么存在?项目结构是什么样子,怎么定位jar 2.Idea 的操作 1.新建maven项目 2.配置仓库 3.添加依赖,添加fastjson的依赖 4.打包项目 3.Maven坐标主要组成 4.maven生命周期 4.1 名词解释 4.2 生命周期 4.3 goal 的概念 4.4 生命周期和phase的关系 5.idea maven的配置 6.POM有2个很重要的关系:聚合.继承 一.聚合 二.继承 7.Maven 中的 profile 8.maven 插件 9.

随机推荐