知识点系列 SpringSecurity 基础

1·介绍

Spring Security是为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架(包含: 认证 , 授权两个方面)。它提供了完整的安全性解决方案,可以在Web请求级别和方法调用级别处理身份认证和授权充分利用了Spring IOC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能。

官网地址: Spring SecurityLevel up your Java code and explore what Spring can do for you.icon-default.png?t=N7T8https://spring.io/projects/spring-security

 2·入门教程

        2.1 工程搭建

                        pom.xml 创建测试工程并引入依赖 ;

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- web起步依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- springBoot整合Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
    <!-- lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
  
</dependencies>

 B: 引导类

@SpringBootApplication
public class MySecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(MySecurityApplication.class,args);
    }
}

C:Controller

@RestController
public class UserController {
    @GetMapping("/hello")
    public String hello(){
        return "hello security";
    }
    @GetMapping("/say")
    public String say(){
        return "say security";
    }
    @GetMapping("/register")
    public String register(){
        return "register security";
    }

D:测试:访问http://localhost:8080/hello

会自动拦截,并跳转到登录页面(SpringSecurity提供),登录之后才可以访问; 而登录的用户名和密码都是SpringSecurity中内置的默认的用户名密码, 用户名为user , 密码为控制台输出的一段随机数;

填写账号密码即可登录。此处密码为默认密码,需要自己按需修改

# 我们也可在配置文件中配置用户名和密码,实际开发中密码不应明文配置
spring.security.user.name=user
spring.security.user.password=6666

2.2 认证配置

【1】自定义合法登录用户信息

如果我们想指定系统的访问用户名及密码, 可以通过配置的形式声明 , 声明一个 UserDetailsService 类型的Bean。

@Configuration
@EnableWebSecurity//开启web安全设置生效
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 构建认证服务,并将对象注入spring IOC容器,用户登录时,会调用该服务进行用户合法信息认证
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        //构建用户
        UserDetails u1 = User
                .withUsername("itcast")
                .password("{noop}123456")                //{noop}意味着密码不以密文形式出现
                .authorities("P1", "ROLE_ADMIN").build();         // p1,role_admin 皆为权限类型
        UserDetails u2 = User
                .withUsername("itheima")
                .password("{noop}123456")
                .authorities("O1", "ROLE_SELLER").build();
        inMemoryUserDetailsManager.createUser(u1);
        inMemoryUserDetailsManager.createUser(u2);
        return inMemoryUserDetailsManager;
    }
}

2.3授权配置

1). 编码方式

@Configuration
@EnableWebSecurity

//用于启用全局的方法安全性。
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("itcast").password("{noop}123456").authorities("P1","ROLE_ADMIN").build());
        inMemoryUserDetailsManager.createUser(User.withUsername("itheima").password("{noop}123456").authorities("O1","ROLE_SELLER").build());
        return inMemoryUserDetailsManager;
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()
            .and()
            .logout()
            .and()
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/register").permitAll() //不登录即可访问
            .antMatchers("/hello").hasAuthority("P1") //具有P1权限才可以访问
            .antMatchers("/say").hasRole("SELLER") //具有SELLER 角色才可以访问
            .anyRequest().authenticated(); //其他的登录之后就可以访问
    }
}

TIPS:

1·CSRF(Cross-site request forgery)跨站请求伪造,也被称为"One Click Attack"或者 Session Riding,通常缩写为 CSRF 或者 XSRF,是一种对网站的恶意利用。  

2·Spring Security 提供了几个注解,如 @PreAuthorize@PostAuthorize@Secured 等,这些注解可以应用于方法上,以定义方法调用前后的安全性要求。

  • @PreAuthorize: 在方法调用前进行权限检查。
  • @PostAuthorize: 在方法调用后进行权限检查,通常用于返回值的过滤。
  • @Secured: 用于简单的基于角色的安全性检查。

2). 注解方式

在控制方法/URL的权限时, 可以通过配置类中配置的方式进行控制, 也可以使用 注解 @PreAuthorize 来进行控制,

 @GetMapping("/hello")
 @PreAuthorize("hasAuthority('P5')")
 public String hello(){
     return "hello security";
 }

@GetMapping("/say")
@PreAuthorize("hasRole('SELLER')")
public String say(){
    return "say security";
}

3)问题所在

A. 密码采用的是明文的,不安全 ;

B. 用户名/密码直接通过程序硬编码,不够灵活 ;

因此,考虑使用密码加密,让敏感数据变得安全及灵活

 2.4 密码加密

2.4.1   可逆加密算法:加密后, 密文可以反向解密得到密码原文;

1). 对称加密
指加密和解密使用相同密钥的加密算法。
优点: 对称加密算法的优点是算法公开、计算量小、加密速度快、加密效率高。 缺点: 没有非对称加密安全。
常见的对称加密算法:DES、3DES、DESX、Blowfish、RC4、RC5、RC6和AES

2). 非对称加密

指加密和解密使用不同密钥的加密算法,也称为公私钥加密。假设两个用户要加密交换数据,双方交换公钥,使用时一方用对方的公钥加密,另一方即可用自己的私钥解密。
加密和解密:
  • 私钥加密,持有私钥或公钥才可以解密
  • 公钥加密,持有私钥才可解密
优点: 非对称加密与对称加密相比,其安全性更好; 缺点: 非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。
简单理解:A向B发送数据,需要用到B的公钥进行加密,加密后的密文发送给B,B使用自己的私钥即可解密。 安全性更高,但是速度慢。

2.4.2 不可逆加密算法

一旦加密就不能反向解密得到密码原文 。通常用于密码数据加密。
常见的不可逆加密算法有: MD5 、SHA、HMAC
2.3.3 MD5与Bcrypt
1).MD5
MD5是比较常见的加密算法,广泛的应用于软件开发中的密码加密,通过MD5生成的密文,是无法解密得到明文密码的。但是现在在大数据背景下,很多的网站通过大数据可以将简单的MD5加密的密码破解。例如通过彩虹表暴力破解。

网址: md5在线解密破解,md5解密加密

可以在用户注册时,限制用户输入密码的长度及复杂度,从而增加破解难度。

2). Bcrypt

用户表的密码通常使用 MD5 等不可逆算法加密后存储,为防止彩虹表破解,会先使用一个特定的字符串(如域名)加密,然后再使用一个随机的 salt(盐值)加密。 特定字符串是程序代码中固定的,salt 是每个密码单独随机,一般给用户表加一个字段单独存储,比较麻烦。

BCrypt 算法将 salt 随机并混入最终加密后的密码,验证时也无需单独提供之前的salt,从而无需单独处理 salt 问题。

验证程序:

~加密密码

BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
for (int i = 0; i < 10; i++) {
    System.out.println(bCryptPasswordEncoder.encode("123456"));
}

~得到结果:

$2a$10$C6YynRFeJsSy7D/kg3d30OWnuwko7KQIEK5JrX0mWND.vuz2TqwpK
$2a$10$aSJfxH2oBtopFMbkMJ.PQ.sbSBXJH9g.9bv1mCyte/BtcU9VTs7lG
$2a$10$nVoB.eV5Uhc9FNUC36Pn0OosGh7aKlp7Sjfxaiml8NCSJ6PX1q6.m
$2a$10$2RM3mRNjz1LoZ5eeLdj.Hu15vlWIIj2zJC09vwTevBlIi5rjJStam
$2a$10$5bTOnk9hITzJd6EJMsX47uX9UdjASrPl4sEG6GJjfZGTk9f/37Q/q
$2a$10$0.PfbDnlBBWzpsw8PBjDcOtjUnwRgbSPCmhrAg5APUWor/4eQ0VVy
$2a$10$jfpPFH0DuTENicQ6vv38BeBO5YUXolS03bk1Ti3fmCrhQmBL1hYj.
$2a$10$pxR.jhV79v1po1vbhWi8CudiLTaw.W5lpl.E/dOEodfGXCJIPrJ4i
$2a$10$MvWb5LvCojzloYX9QLA8buL2Mkci2qaiMIdIH2PzGDssHUzEU21R2
$2a$10$7HLclohKrBZHvsBLDm8U/eTqe0KP2qV4F9d6jNvP4vO0pJG4wmeQy 

 ~验证密码

BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
boolean matches = 
   bCryptPasswordEncoder.matches("123456", "$2a$10$c2sZT/LtM1ExWfZjO0yIPeTGSqMSlX7oi.SvliMbeZpT9Y4qIBDue");
System.out.println(matches);//返回值为true, 则代表验证通过; 反之, 验证不通过

2.5        程序完善

2.5.1 密码加密处理

在配置类 SecurityConfig中配置Bean:

//配置密码加密器 ;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
    return new BCryptPasswordEncoder();
}

2.5.2  动态查询用户

创建简易数据库

create database security_demo default charset=utf8mb4;
use security_demo;

CREATE TABLE `tb_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(100) DEFAULT NULL,
  `password` varchar(100) DEFAULT NULL,
  `roles` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `tb_user` VALUES (1, 'itcast', '$2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC', 'ROLE_ADMIN,P1');
INSERT INTO `tb_user` VALUES (2, 'itheima', '$2a$10$f43iK9zKD9unmgLao1jqI.VluZ.Rr/XijizVEA73HeOu9xswaUBXC', 'ROLE_SELLER,O1');

  2.5.3 导入依赖

        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.4</version>
        </dependency>

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

2.5.4 application.yml文件

# 应用名称
spring.application.name=security_test
# 应用服务 WEB 访问端口
server.port=8080

# 配置用户名和密码
#spring.security.user.name=user
#spring.security.user.password=6666

#下面这些内容是为了让MyBatis映射
#指定Mybatis的Mapper文件
mybatis.mapper-locations=classpath:mapper/*xml
#指定Mybatis的实体目录
mybatis.type-aliases-package=com.itheima.security.pojo

# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库连接地址
spring.datasource.url=jdbc:mysql://192.168.188.130:3306/security_demo?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=root

 TIPS: 关于实体类与Mapper类,可以考虑使用MyBatisX插件来实现

2.5.6实体类


@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class TbUser implements Serializable {

    private Integer id;


    private String username;
    private String password;
    private String roles;

    private static final long serialVersionUID = 1L;
}

2.5.7  mapper

@Mapper
public interface TbUserMapper {

    int deleteByPrimaryKey(Long id);

    int insert(TbUser record);

    int insertSelective(TbUser record);

    TbUser selectByPrimaryKey(Long id);

    int updateByPrimaryKeySelective(TbUser record);

    int updateByPrimaryKey(TbUser record);
  
    TbUser findByUserName(@Param("userName") String userName);

}

xml:篇幅有限省略。

2.5.8 自定义需要的实现类

package com.itheima.security.config;

import com.itheima.security.mapper.TbUserMapper;
import com.itheima.security.pojo.TbUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.List;


@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private TbUserMapper tbUserMapper;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        TbUser user = tbUserMapper.findByUserName(userName);
        if (user==null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        //构建认证明细对象
        //获取用户权限
        List<GrantedAuthority> list = AuthorityUtils.commaSeparatedStringToAuthorityList(user.getRoles());
        User user1 = new User(user.getUsername(),user.getPassword(),list);
        return user1;
    }
}

该方法只适用于权限以逗号分隔的

AuthorityUtils.commaSeparatedStringToAuthorityList():
这是一个来自Spring Security的工具方法,用于将逗号分隔的字符串转换为GrantedAuthority对象的列表。GrantedAuthority是Spring Security中表示权限或角色的接口。

 

 

  • 14
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security是一个强大的身份验证和授权框架,用于保护Spring应用程序的安全性。下面是Spring Security的基础用法和一些常见知识点的详解: 1. 配置Spring Security:在Spring项目中,需要添加Spring Security的依赖,并配置相应的安全配置类(继承自 `WebSecurityConfigurerAdapter`),通过重写方法来配置身份验证和授权规则。 2. 身份验证(Authentication):Spring Security提供了多种身份验证方式,包括基于表单、HTTP基本认证、OAuth等。可以通过配置认证提供者(Authentication Provider)来定义如何验证用户的身份。 3. 授权(Authorization):通过配置访问控制规则,可以限制用户对特定资源的访问权限。Spring Security提供了多种授权方式,如基于角色、权限、表达式等。 4. 用户和角色管理:Spring Security可以与数据库或其他存储系统集成,用于管理用户信息和角色。可以自定义用户详细信息服务(UserDetailsService)和密码编码器(PasswordEncoder)来获取用户信息和验证密码。 5. 登录和注销:通过配置登录页面和注销功能,用户可以进行登录和退出操作。Spring Security提供了默认的登录页面和注销处理,也可以自定义实现。 6. CSRF防护:Spring Security默认开启CSRF(跨站请求伪造)防护功能,可以防止恶意网站利用用户的身份发起跨站请求。可以通过配置全局或特定URL的CSRF保护。 7. Remember-Me功能:Spring Security提供了Remember-Me功能,允许用户在下次访问时自动登录。可以通过配置持久化令牌的方式实现。 8. 安全事件和日志:Spring Security可以记录安全相关的事件和日志,包括认证成功、失败、访问被拒绝等。可以通过自定义的 `ApplicationListener` 或配置日志记录器来处理和记录这些事件。 9. 自定义过滤器和拦截器:通过自定义过滤器或拦截器,可以在请求处理过程中添加额外的安全逻辑。Spring Security提供了多个预定义的过滤器和拦截器,也可以自定义实现。 10. 方法级安全性:Spring Security支持方法级别的安全性控制,可以通过注解(如 `@Secured`、`@PreAuthorize`)或表达式(如 `hasRole()`、`hasPermission()`)来限制方法的访问权限。 11. 扩展和定制:Spring Security提供了丰富的扩展点,可以根据需求进行定制开发。例如,自定义认证提供者、用户详细信息服务、访问决策管理器等。 这些是Spring Security的基础用法和常见知识点的概述。具体使用时,可以根据项目需求和实际情况进行配置和定制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值