SpringBoot 集成 SpringSecurity 详解(六)-- 基于MySql实现角色授权

需求缘起

上一小节中,我们基于内存实现了角色授权,这一节我们就通过MySql数据库来操作。

本节 demo

技术要点

实现 UserDetailsService 接口中的 loadUserByUsername() 方法,在该方法中的主要逻辑是,将数据库中的用户名,密码和权限信息(现在先不考虑权限相关的知识点)传给 SpringSecurity ,由SpringSecurity 进一步处理。

Spring Data JPA进行操作数据库,关于 Spring JPA 的使用可以参考我的另一篇博文一起学Springboot – 第四节 Spring-data-jpa 操作MySQL数据库(一)

1.添加依赖

添加MySQL数据库依赖,我们在创建项目的时候已经添加过了,没添加过的再添加即可

        <!--MySql 依赖,要同步本地安装的数据库版本,我这里是8.0.11-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
            <version>8.0.11</version>
        </dependency>
        <!Spring data jpa 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
       <!--这里使用阿里巴巴数据连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>

2. 添加数据库配置

在application.yml 中添加数据库相关配置

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/security?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
    username: root
    password: 123456
  jpa:
   show-sql: true
   hibernate:
     ddl-auto: update
   database: mysql

3.创建实体类

3.1 创建用户信息实体 userinfo

@Data
@Entity
public class UserInfo {

    @Id//用户id
    @GeneratedValue(strategy = GenerationType.IDENTITY)//自增
    private Long id;
      //角色id
    private Long rid;
    //用户名
    private String username;
    //用户密码
    private String password;
}

3.2 创建角色信息实体RoleInfo

@Entity
@Data
public class RoleInfo {
    @Id//角色id
    @GeneratedValue(strategy = GenerationType.IDENTITY)//自增
    private Long id;
    //角色名
    private String name;
    //该角色所拥有的权限名称
    private String authority;
}

4.创建操作数据库接口层 Repository

4.1 UserInfoRepository

@Repository
public interface UserInfoRepository extends JpaRepository<UserInfo, Long> {
    /**
     * 通过用户名获取用户信息
     * @param username
     * @return
     */
    UserInfo findByUsername(String username);
}

4.2 RoleInfoRepository

@Repository
public interface RoleInfoRepository extends JpaRepository<RoleInfo, Long> {
}

5. 实现业务逻辑

5.1 实现角色的创建和通过id获取角色信息

public interface RoleInfoService {

    /**
     * 创建角色
     * @param userInfo
     * @return
     */
    RoleInfo create(RoleInfo userInfo);

    /**
     * 通过id获取角色信息
     * @param id
     * @return
     */
    RoleInfo findById(Long id);
}
@Service
public class RoleInfoServiceImpl implements RoleInfoService {

    @Autowired
    private RoleInfoRepository roleInfoRepository;

    /**
     * 创建角色
     *
     * @param userInfo
     * @return
     */
    @Override
    public RoleInfo create(RoleInfo userInfo) {
        return roleInfoRepository.save(userInfo);
    }

    /**
     * 通过id获取角色信息
     *
     * @param id
     * @return
     */
    @Override
    public RoleInfo findById(Long id) {
        return roleInfoRepository.findById(id).orElse(null);
    }
}

5.2 实现用户信息的创建和通过用户名获取用户信息

UserInfoService 接口

public interface UserInfoService {

    /**
     * 创建新用户
     * @param userInfo
     * @return
     */
    UserInfo create(UserInfo userInfo);

    /**
     * 通过用户名查找用户信息
     * @param username
     * @return
     */
    UserInfo findByUsername(String username);
}

UserInfoService 接口实现类
提示: 在添加用户信息时,记得要对密码加密

package com.security.demo.service.impl;

import com.security.demo.entity.UserInfo;
import com.security.demo.repository.UserInfoRepository;
import com.security.demo.service.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserInfoServiceImpl implements UserInfoService {

    @Autowired
    private UserInfoRepository userInfoRepository;

    @Autowired
    private PasswordEncoder passwordEncoder;

    /**
     * 创建新用户
     *
     * @param userInfo
     * @return
     */
    @Override
    public UserInfo create(UserInfo userInfo) {
    	//密码加密
    	userInfo.setPassword(passwordEncoder.encode(userInfo.getPassword()));
        return userInfoRepository.save(userInfo);
    }

    /**
     * 通过用户名查找用户信息
     *
     * @param username
     * @return
     */
    @Override
    public UserInfo findByUsername(String username) {
        return userInfoRepository.findByUsername(username);
    }
}

在springSecurity 5.x之后密码需要加密,当创建用户信息的时候加密,还需要再配置,其实在上一节中我们使用过了,代码如下

@Configuration
@EnableWebSecurity//开启Spring Security的功能
public class SecurityConfig  extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        //SpringSecurity 提供的一种编码器,我们也可以自己实现PasswordEncoder
        return new BCryptPasswordEncoder();
    }
}

6.添加角色信息和用户信息

6.1 添加角色信息

@Slf4j
public class RoleInfoServiceImplTest extends SpringSecurityDemoApplicationTests {

    @Autowired
    private RoleInfoService roleInfoService;

    @Test
    public void create() {
        RoleInfo roleInfo1 = new RoleInfo();
        roleInfo1.setId(1L);
        roleInfo1.setName("管理员");
        roleInfo1.setAuthority("ADMIN");
        roleInfoService.create(roleInfo1);

        RoleInfo roleInfo2 = new RoleInfo();
        roleInfo2.setId(2L);
        roleInfo2.setName("普通用户");
        roleInfo2.setAuthority("USER");
        roleInfoService.create(roleInfo2);
    }

    @Test
    public void findById() {
        RoleInfo result = roleInfoService.findById(1L);
        log.info("result:{}", result);
    }
}

6.2 添加用户信息

@Component
@Slf4j
public class UserInfoServiceImplTest extends SpringSecurityDemoApplicationTests {

    @Autowired
    private UserInfoService userInfoService;

    @Test
    public void create() {
          UserInfo userInfo1 = new UserInfo();
        userInfo1.setRid(1L);
        userInfo1.setUsername("admin");
        userInfo1.setPassword("123456");
        UserInfo userInfoPO1 = userInfoService.create(userInfo1);
        log.info("userInfoPO1={}", userInfoPO1);

        UserInfo userInfo2 = new UserInfo();
        userInfo2.setRid(2L);
        userInfo2.setUsername("user");
        userInfo2.setPassword("123456");
        UserInfo userInfoPO2 = userInfoService.create(userInfo2);
        log.info("userInfoPO2={}", userInfoPO2);
    }

    @Test
    public void findByUsername(){
        UserInfo result = userInfoService.findByUsername("admin");
        log.info("result:{}", result);
    }
}

7.实现认证和授权

实现认证和授权在 UserDetailsService 接口中的 loadUserByUsername() 方法中进行,
这个也是这一小节的核心,这个方法的作用是将用户名、密码和该用户的权限传递给 Spring Security

@Component
@Slf4j
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private RoleInfoService roleInfoService;

    /**
     * 在这个方法中实现用户身份认证和授权
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        log.info("loadUserByUsername->username={}", username);
        UserInfo userInfo = userInfoService.findByUsername(username);
        if (userInfo == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        List<GrantedAuthority> authorities = new ArrayList<>();

        RoleInfo roleInfo = roleInfoService.findById(userInfo.getRid());
        log.info("roleInfo={}",roleInfo);
        if (roleInfo != null) {
            // 用户可以访问的资源名称(或者说用户所拥有的权限) 注意:必须"ROLE_"开头
            authorities.add(new SimpleGrantedAuthority("ROLE_" + roleInfo.getAuthority()));
        }
        //注意要在创建用户信息的时候密码需要加密
        User userDetails = new User(userInfo.getUsername(), userInfo.getPassword(), authorities);

        return userDetails;
    }
}

8.编写控制器

@RestController
@RequestMapping("/hello")
public class HelloSecurityController {

    @GetMapping
    public String hello(){
        return "Hello Spring Security!";
    }

    /**
     * 在这里例子中 用户 ADMIN和 USER 都能访问这个接口
     * @return
     */
    @GetMapping("/helloUser")
    @PreAuthorize("hasAnyRole('ADMIN','USER')")//访问这个接口需要有 ADMIN或者USER的权限的用户才能访问
    public String user() {
        return "Hello,user";
    }

    /**
     * 在这里例子中 用户 USER是不能访问这个接口的,会报权限不足异常
     * @return
     */
    @GetMapping("/helloAdmin")
    @PreAuthorize("hasAnyRole('ADMIN')")//访问这个接口需要有 ADMIN的权限的用户才能访问
    public String admin() {
        return "Hello,admin";
    }
}

9.Security 配置

不要忘了配置

@Configuration
@EnableWebSecurity//开启Spring Security的功能
@EnableGlobalMethodSecurity(prePostEnabled=true)//开启方法安全级别的控制
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        //SpringSecurity 提供的一种编码器,我们也可以自己实现PasswordEncoder
        return new BCryptPasswordEncoder();
    }
}

10.测试

  1. 重启应用,访问http://localhost:8080/hello/helloUser,输入用户名 “user” 和 密码 “123456”,不出意外将会顺利访问,返回 “Hello,user”;
  2. 此时再去访问 http://localhost:8080/hello/helloAdmin,会报403异常,因为这个接口用户"user"是没有权限访问的;
  3. 重启应用,访问http://localhost:8080/hello/helloUser,输入用户名 “admin” 和 密码 “123456”,不出意外将会顺利访问,返回 “Hello,user”,因为这个接口用户"admin"也有权限访问;
  4. 此时再去访问 http://localhost:8080/hello/helloAdminr,会发现也能顺利访问,因为这个接口用户"admin"有权限访问;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值