Spring Security之Spring Security授权和如何将数据存入数据库

1、授权

所谓的授权,就是用户如果要访问某一个资源,我们要去检查用户是否具备这样的权限,如果具备就允许访问,如果不具备,则不允许访问

1.1配置授权

基于内存配置测试用户,我们有两种方式,第一种就是我们本系列前面几篇文章用的配置方式,如下:

 @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("heng").password("123456").roles("admin")
        .and()
        .withUser("hengheng").password("123456").roles("user");
    }

由于 Spring Security 支持多种数据源,例如内存、数据库、LDAP 等,这些不同来源的数据被共同封装成了一个 UserDetailService 接口,任何实现了该接口的对象都可以作为认证数据源。

因此我们还可以通过重写 WebSecurityConfigurerAdapter 中的 userDetailsService 方法来提供一个 UserDetailService 实例进而配置多个用户:

@Override
    @Bean
    protected UserDetailsService userDetailsService() {
       //InMemoryUserDetailsManager 在内存中设置用户权限
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("heng").password("123").roles("admin").build());
        manager.createUser(User.withUsername("hengheng").password("123").roles("user").build());
        return manager;
    }

两种基于内存定义用户的方法,大家任选一个。

测试用户准备好了,接下来我们准备三个测试接口。如下:

@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

    @GetMapping("/admin/hello")
    public String admin() {
        return "admin";
    }

    @GetMapping("/user/hello")
    public String user() {
        return "user";
    }
}

这三个测试接口,我们的规划是这样的:

  1. /hello 是任何人都可以访问的接口

  2. /admin/hello 是具有 admin 身份的人才能访问的接口

  3. /user/hello 是具有 user 身份的人才能访问的接口

  4. 所有 user 能够访问的资源,admin 都能够访问

接下来我们来配置权限的拦截规则,在 Spring Security 的 configure(HttpSecurity http) 方法中,代码如下:

http.authorizeRequests()
        .antMatchers("/admin/**").hasRole("admin")
        .antMatchers("/user/**").hasRole("user")
        .anyRequest().authenticated()
        .and()
1.2 角色继承

在前面松哥提到过一点,所有 user 能够访问的资源,admin 都能够访问,很明显我们目前的代码还不具备这样的功能。

要实现所有 user 能够访问的资源,admin 都能够访问,这涉及到另外一个知识点,叫做角色继承。

这在实际开发中非常有用。

上级可能具备下级的所有权限,如果使用角色继承,这个功能就很好实现,我们只需要在 SecurityConfig 中添加如下代码来配置角色继承关系即可:

@Bean
RoleHierarchy roleHierarchy() {
    RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
    hierarchy.setHierarchy("ROLE_admin > ROLE_user");
    return hierarchy;
}

2、将数据存入数据库中

UserDetailService

spring Security 支持多种不同的数据源,这些不同的数据源最终都将被封装成 UserDetailsService 的实例,我们是自己来创建一个类实现 UserDetailsService 接口,除了自己封装,我们也可以使用系统默认提供的 UserDetailsService 实例,例如上篇文章和大家介绍的 InMemoryUserDetailsManager 。

我们来看下 UserDetailsService 都有哪些实现类:

可以看到,在几个能直接使用的实现类中,除了 InMemoryUserDetailsManager 之外,还有一个 JdbcUserDetailsManager,使用 JdbcUserDetailsManager 可以让我们通过 JDBC 的方式将数据库和 Spring Security 连接起来。

JdbcUserDetailsManager
JdbcUserDetailsManager 自己提供了一个数据库模型,这个数据库模型保存在如下位置:

org/springframework/security/core/userdetails/jdbc/users.ddl

这里存储的脚本内容如下:

create table users(username varchar(50) not null primary key,password varchar(500) not null,enabled boolean not null);
create table authorities (username varchar(50) not null,authority varchar(50) not null,constraint fk_authorities_users foreign key(username) references users(username));
create unique index ix_auth_username on authorities (username,authority);

配置完成后,接下来,我们将上篇文章中通过 InMemoryUserDetailsManager 提供的用户数据用 JdbcUserDetailsManager 代替掉,如下:


@Resource
    private DataSource dataSource;

@Bean
    protected UserDetailsService userDetailService() {

        //基于数据库来配置用户权限
        JdbcUserDetailsManager jdbcUserDetailsManager = new JdbcUserDetailsManager();
        jdbcUserDetailsManager.setDataSource(dataSource);
        if (!jdbcUserDetailsManager.userExists("jiangheng")) {
            jdbcUserDetailsManager.createUser(User.withUsername("jiangheng").password("123456").roles("admin").build());
        }
        if (!jdbcUserDetailsManager.userExists("hengheng")) {
            jdbcUserDetailsManager.createUser(User.withUsername("hengheng").password("123456").roles("user").build());
        }
        return jdbcUserDetailsManager;
    }

这段配置的含义如下:

  1. 首先构建一个 JdbcUserDetailsManager 实例。

  2. 给 JdbcUserDetailsManager 实例添加一个DataSource 对象。

  3. 调用 userExists方法判断用户是否存在,如果不存在,就创建一个新的用户出来(因为每次项目启动时这段代码都会执行,所以加一个判断,避免重复创建用户)。

  4. 用户的创建方法和我们之前 InMemoryUserDetailsManager 中的创建方法基本一致。

这里的 createUser 或者 userExists 方法其实都是调用写好的 SQL 去判断的,我们从它的源码里就能看出来(部分):

public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsManager,
  GroupManager {
 public static final String DEF_USER_EXISTS_SQL = "select username from users where username = ?";

 private String userExistsSql = DEF_USER_EXISTS_SQL;

 public boolean userExists(String username) {
  List<String> users = getJdbcTemplate().queryForList(userExistsSql,
    new String[] { username }, String.class);

  if (users.size() > 1) {
   throw new IncorrectResultSizeDataAccessException(
     "More than one user found with name '" + username + "'", 1);
  }

  return users.size() == 1;
 }
 }

从这段源码中就可以看出来,userExists 方法的执行逻辑其实就是调用 JdbcTemplate 来执行预定义好的 SQL 脚本,进而判断出用户是否存在,其他的判断方法都是类似,我就不再赘述。

数据库支持

通过前面的代码,大家看到这里需要数据库支持,所以我们在项目中添加如下两个依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

配置application.properties

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/security_demo?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root

参考文章 江南一点雨公众号文章

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值