springsecurity权限管理

一、Spring Security概述

1,Spring Security简介

Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在 Spring 应用上下文中配置的 Bean,充分利用了 Spring IoC(Inversion of Control 控制反转),DI(Dependency Injection 依赖注入)和 AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

Spring Security 拥有以下特性:

  • 对身份验证和授权的全面且可扩展的支持
  • 防御会话固定、点击劫持,跨站请求伪造等攻击
  • 支持 Servlet API 集成
  • 支持与 Spring Web MVC 集成

Spring、Spring Boot 和 Spring Security 三者的关系如下图所示:

请添加图片描述

2、Spring Security快速入门

2.1、引入依赖

<!--springboot整合security坐标-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

或者是在创建springboot项目时勾选请添加图片描述

2.2、创建一个控制器

@RestController
public class HelloController {
    
    
    @GetMapping("hello")
    public String hello(){
    
    
        return "Hello Spring security";
    }
}

2.3、启动项目

访问:http://localhost:8080/hello 结果打开的是一个登录页面,其实这时候我们的请求已经被保护起来了,要想访问,需要先登录。

请添加图片描述

Spring Security 默认提供了一个用户名为 user 的用户,其密码在控制台可以找到请添加图片描述

四、Spring Security 认证配置

1、WebSecurityConfigurerAdapter

当然还可以通过配置类的方式进行配置,创建一个配置类去继承,实现自定义用户名密码登录

/**
 * Spring Security配置类
 * 在springboot2.7 后WebSecurityConfigurerAdapter弃用了,用2.5.4
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
        // 对密码进行加密。123 是密码明文,现在 Spring Security 强制性要求『不允许明文存储密码』。
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123");
        auth.inMemoryAuthentication().withUser("tom").password(password).roles("admin");
    }
}

springsecurity强制性要求必须使用密码加密器(PasswordEncoder)对原始密码(注册密码)进行加密。因此,如果忘记指定 PasswordEncoder 会导致执行时会出现 There is no PasswordEncoder mapped for the id "null" 异常。

这是因为我们在对密码加密的时候使用到了 BCryptPasswordEncoder 对象,而 Spring Security 在对密码比对的过程中不会『自己创建』加密器,因此,需要我们在 Spring IoC 容器中配置、创建好加密器的单例对象,以供它直接使用。

所以,我们还需要在容器中配置、创建加密器的单例对象(上面那个 new 理论上可以改造成注入),修改Spring securitry配置类

/**
 * Spring Security配置类
 * 在springboot2.7后WebSecurityConfigurerAdapter弃用了,用2.5.4
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
        // 对密码进行加密。123 是密码明文,现在 Spring Security 强制性要求『不允许明文存储密码』。
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123");
        //此方法是在代码中写死的,无法从数据库中获取,所以基本不适用
        auth.inMemoryAuthentication().withUser("tom").password(password).roles("admin");
    }
       /**
     * 将PasswordEncoder注入到ioc容器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
    
    
        return new BCryptPasswordEncoder();
    }
}

Spring Security 内置的 Password Encoder 有:

加密算法名称 PasswordEncoder
NOOP NoOpPasswordEncoder.getInstance()
SHA256 new StandardPasswordEncoder()
BCRYPT(官方推荐) new BCryptPasswordEncoder()
LDAP new LdapShaPasswordEncoder()
PBKDF2 new Pbkdf2PasswordEncoder()
SCRYPT new SCryptPasswordEncoder()
MD4 new Md4PasswordEncoder()
MD5 new MessageDigestPasswordEncoder(“MD5”)
SHA_1 new MessageDigestPasswordEncoder(“SHA-1”)
SHA_256 new MessageDigestPasswordEncoder(“SHA-256”)

2、从数据库中获取信息

  • 在pom中添加依赖
 <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!-- mysql-connector -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.32</version>
        </dependency>
  • 在yml中配置
server:
  port: 8070
spring:
  # 配置数据源信息
  datasource:
    # 配置数据源类型
    type: com.zaxxer.hikari.HikariDataSource
    # 配置连接数据库信息
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true
    username: root
    password: 123456
mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.woniu.entity
  configuration:
    # 配置MyBatis日志,执行sql的时候,将sql打印到控制台
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  • 在entity包下新建实体类Users,建议不要用User,因为UserDetails 接口中有一个实现类叫User,不止一次会导错包
package com.woniu.entity;

import lombok.Data;

import java.util.List;
@Data
public class Users {
    
    

   private Integer id;

   private String username;

   private String account;

   private String password;

   private List<String> anths;//该用户拥有的权限

}
  • 在数据库中新建表
    请添加图片描述
    User表
    请添加图片描述
    关联表
    请添加图片描述
    权限表
    请添加图片描述
  • 在mapper包下创建UsersMapper
package com.woniu.dao;

import com.woniu.entity.Users;

public interface UsersDao {
    
    
        /**
         * 通过账号获取用户信息
         * @param account 
         * @return
         */
        Users getUserInfoByAccount(String account);
}

  • 在resources下新建mapper包,创建UsersMapper.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.woniu.mapper.UsersMapper">
    <resultMap id="userMap" type="Users">
        <result column="id" property="id"></result>
        <result column="username" property="username"></result>
        <result column="account" property="account"></result>
        <result column="password" property="password"></result>
        <collection property="anths" ofType="java.lang.String">
            <result column="anth_code"></result>
        </collection>
    </resultMap>
    <select id="getUserInfoByAccount" resultMap="userMap">
        SELECT
            us.id,
            us.username,
            us.account,
            us.password,
            ta.anth_code
        FROM
            t_users us
                left join t_user_anth tua on us.id = tua.user_id
                left join t_anth ta on tua.anth_id = ta.id
        where account = #{account}
    </select>
</mapper>
  • 在service包下创建一个MyUserDetailsService 类实现UserDetailsService 接口

    /**
     * spring security认证业务类
     */
    @Service
    public class MyUserDetailsService implements UserDetailsService {
          
          
       
        @Autowired
        private UsersMapper usersMapper;
    
        @Autowired
        private PasswordEncoder passwordEncoder;
        
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
          
          
               //调用dao到数据库中根据username查找用户信息
            Users users = usersMapper.getByUserName(username);
            String anths = String.join(",", userInfo.getAnths());
             try {
          
          
                //将查找到的用户帐号与密码保存到Security的User对象中由Security进行比对
                return new User(users.getUsername(), passwordEncoder.encode(users.getPassword()),
                        //配置登录用户有哪些角色和权限,此处模拟直接写死
                        AuthorityUtils.commaSeparatedStringToAuthorityList(anths);
            }catch (Exception e){
          
          
                throw  new UsernameNotFoundException("用户"+username+"不存在");
            }
        }
    }
    
  • 修改spring security配置类

    
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
          
          
    
        @Autowired
        private MyUserDetailsService userDetailsService;
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          
          
           auth.userDetailsService(userDetailsService);
        }
         
        /**
         * 将PasswordEncoder注入到ioc容器
         * @return
         */
        @Bean
        public PasswordEncoder passwordEncoder() {
          
          
            return new BCryptPasswordEncoder();
        }
    }
    
  • 启动项目,在浏览器中测试
    请添加图片描述

猜你喜欢

转载自blog.csdn.net/lanlan112233/article/details/129737056