(二十一)Spring Boot 安全管理【Spring Security 基本配置】

       安全可以说是公司的红线了, 一般项目都会有严格的认证和授权操作,在 Java 开发领域常见安全框架有 Shiro Spring Security。Shiro 一个轻量级的安全管理框架,提供了认证、授权、会话管理、密码管理、缓存管理等功能, Spring Security 是一个相对复杂的安全管理框架,功能比 Shiro 更加强大,权限控制细粒度更高,对 OAuth 2 的支持也更友好,又因为 Spring Security 源自 Spring 家族,因此可以和 Spring 框架无缝整合,特别是 Spring Boot 中提供的自动化配置方案,可以让 Spring Security 的使用更加便捷。

1、基本用法

1.1 创建项目,添加依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

1.2 添加 hello 接口

@RestController
public class HelloController {

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

1.3 启动项目测试

项目启动成功后,访问/hello 接口会自动跳转到登录页面,这个登录页面是由 Spring Security 提供的。
在这里插入图片描述
默认的用户名是 user ,默认的登录密码则在每次启动项目时随机生成 查看项目启动日志,
在这里插入图片描述

2、配置用户名和密码

       如果开发者对默认的用户名和密码不满意,可以在 application.properties 中配置默认的用户名、密码以及用户角色,配置方式如下:

spring.security.user.name=wi-gang
spring.security.user.password=123456
spring.security.user.roles=admin

当开发者在 application.properties 中配置了默认的用户名和密码后,再次启动项目,项目启动日志就不会打印出随机生成的密码了,用户可直接使用配置好的用户名和密码登录,登录成功后,用户还具有 个角色一-admin

3、基于内存的认证

       开发者也可以自定义类继承自 WebSecurityConfigurerAdapter ,进而实现对 Spring Security 更多的自定义配置,例如基于内存的认证,配置方式如下:

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;


@CacheConfig
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }

    /**
     * 功能描述:,在该方法中配直两个用户,一个用户名是 admin ,密码是 123456,具备两个角色 ADMIN 和 USER,另一个用户名是 wi-gang ,密码是 123456 ,具备一个角色 USER
     * @author wi-gang
     * @date 2022/1/23 11:28 下午
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("admin").password("123456").roles("ADMIN","USER")
                .and()
                .withUser("wi-gang").password("123456").roles("USER");
    }
}

4、HttpSecurity

       虽然现在可以实现认证功能,但是受保护的资源都是默认的 ,而且也不能根据实际情况进行角色管理,如果要实现这些功能 就需要重写 WebSecurityConfigurerAdapter 的另一个方法,代码如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;


@Configuration
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    /**
     * 功能描述:在该方法中配直两个用户,一个用户名是 admin ,密码是 123456,具备两个角色 ADMIN 和 USER,另一个用户名是 wi-gang ,密码是 123456 ,具备一个角色 USER
     * @author wi-gang
     * @date 2022/1/23 11:28 下午
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("root").password("123").roles("ADMIN", "DBA")
                .and()
                .withUser("admin").password("123456").roles("ADMIN", "USER")
                .and()
                .withUser("wi-gang").password("123456").roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("admin/**")
                .hasRole("ADMIN")
                .antMatchers("/user/**")
                .access("hasAnyRole('ADMIN','USER')")
                .antMatchers("/db/**")
                .access("hasRole('ADMIN') and hasRole('USER')")
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginProcessingUrl("/login")
                .permitAll()
                .and()
                //关闭 csrf
                .csrf()
                .disable();
    }
}

代码解释:

  • 首先配置三个用户, root 用户具备 ADMIN、DBA 的角色, admin用户具备 ADMIN、USER 的角色, wi-gang 用户具备 USER 的角色
  • authorizeRequests()方法开启 HttpSecurity 的配置,antMatchers() 方法到access()方法分别表示用户访问admin/**模式的 URL 必须具备 ADMIN 的角色;用户访问/user/**模式的 URL必须具备 ADMIN 或 USER 的角色;用户访问/db/**模式的 URL 必须具备 ADMIN 和 DBA 的角色。
  • anyRequest()authenticated()表示除了前面定义的 URL 模式之外,用户访问其他的 URL 都必须认证后访问(登录后访问)
  • formLogin() .loginProcessingUrl("/login").permitAll()几行表示开启表单登录,即项目启动后看到的登录页面,同时配置了登录接口为/login,即可以直接调用/login接口,发起一个 POST 请求进行登录,登录参数中用户名必须命名为 username ,密码必须命名为 password ,配置 loginProcessingUrl 接口主要是方使 AJax 或者移动端调用登录接口 。最后还配置了 permitAll() ,表示和登录相关的接口都不需要认证即可访问。

配置完成后,接下来在 Controller 中添加如下接口进行测试:

@RestController
public class HelloController {

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

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

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

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

根据上面的配置, /admin/hello 接口 root 和 admin 用户具有访问权限;/user/hello 接口admin 和 wi-gang 用户具有访问权限:/db/hello 路径则只有 root 用户具有访问权限。

5、密码加密

5.1 为什么要加密

       一般密码账户都是保存在数据库中,如果你的数据库被黑客入侵被盗了,如果你不加密,那么你的账户被盗了,就危险了。而加密的密码就不同了,因为黑客得到的只是你加密后的字符串,而对方可能不知道你的加密算法,破解难度增加,这样你的账户安全性就大大增加了。

5.2 加密方案

       密码加密一般会用到散列函数,又称散列算法、哈希函数,这是一种从任何数据中创建数字“指纹”的方法。散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来,然后将数据打乱混合,重新创建一个散列值。散列值通常用一个短的随机字母和数字组成的字符串来代表。好的散列函数在输入域中很少出现散列冲突。在散列表和数据处理中,不抑制冲突来区别数据会使得数据库记录更难找到。我们常用的散列函数有 MD5 消息摘要算法、安全散列算法(Secure Hash Algorithm )。
       但是仅仅使用散列函数还不够,为了增加密码的安全性,一般在密码加密过程中还需要加盐,所谓的盐可以是一个随机数,也可以是用户名,加盐之后,即使密码明文相同的用户生成的密码,密文也不相同,这可以极大地提高密码的安全性。但是传统的加盐方式需要在数据库中有专门的字段来记录盐值,这个字段可能是用户名字段(因为用户名唯 ),也可能是一个专门记录盐值的字段,这样的配置比较烦琐 Spring Security 提供了多种密码加密方案,官方推荐使用 BCryptPasswordEncoder, BCryptPasswordEncoder 使用 BCrypt 强哈希函数,开发者在使用时可以选择提供由 strength 和SecureRandom 实例。strength 越大,密钥的迭代次数越多,密钥迭代次数为2^strength。 strength 取值在 4~31 之间,默认为 10。

5.3 实操

  • Spring Boot 中配置密码加密非常容易,只需要修改上文配置的 PasswordEncoder 这个 Bean
    的实现即可,代码如下:
@Bean
PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder(10);
}
 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     auth.inMemoryAuthentication()
             .withUser("root").password("123").roles("ADMIN", "DBA")
             .and()
             .withUser("admin").password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq").roles("ADMIN", "USER")
             .and()
             .withUser("wi-gang").password("$2a$10$eUHbAOMq4bpxTvOVz331IehLe3fu6NwqC9tdOcxJXEhyZ4simqXTC").roles("USER");
 }

这里的密码就是使用 BCryptPasswordEncoder 加密后的密码,虽然 admin、wi-gang 加密后的密
码不一样, 但是明文都是 123 。配置完成后,使用 admin/123 或 wi-gang/123 就可以实现登录。本案
例使用了配置在内存中的用户,一般情况下,用户信息是存储在数据库中的,因此需要在用户注册时对密码进行加密处理,代码如下:

@Service
public class RegService {
    public int reg(String username , String password) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(10);
        String encodePasswod = encoder.encode(password);
        return saveToDb(username, encodePasswod);
    }
}

用户将密码从前端传来之后 通过调用 BCryptPasswordEncoder 实例中的 encode 方法对密码进行加密处理,加密完成后将密文存入数据库。

6、方法安全

开发者也可以通过注解来灵活地配置方法安全,要使用相关注解,首先要通过@EnableGloba!MethodSecurity 注解开启基于注解的安全配置:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
public class MyWebSecurityConfig {
}

代码解释:

  • prePostEnabled = true 会解锁@PreAuthorize 和 @PostAuthorize 两个注解,@PreAuthorize 注解会在方法执行前进行验证,而@PostAuthorize 注解在方法执行后进行验证。
  • securedEnabled = true 会解锁@Secured 注解。

开启注解安全配置后,接下来创建 MethodService 进行测试,代码如下:

@Service
public class MethodService {

    @Secured("ROLE_ADMIN")
    public String admin() {
        return "admin";
    }

    @PreAuthorize("hasRole('ADMIN') and hasRole('DBA')")
    public String adminAndDBA() {
        return "adminAndDBA";
    }

    @PreAuthorize("hasAnyRole('ADMIN','DBA','USER')")
    public String hasAnyRole() {
        return "hasAnyRole";
    }
}

代码解释:

  • @Secured(”ROLE_ ADMIN")注解表示访问该方法需要 ADMIN 角色,注意这里需要在角色前加一个前缀ROLE_
  • @PreAuthorize("hasRole('ADMIN') and hasRole('DBA')") 注解表示访问该方法既需要 ADMIN又需要 DBA 角色
  • @PreAuthorize("hasAnyRole('ADMIN','DBA','USER')")表示访问该方法需要 ADMIN、DBA、USER 角色
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值