欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

(二十一)SpringBoot安全管理【SpringSecurity基本配置】

时间:2023-07-08

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

1、基本用法 1.1 创建项目,添加依赖

org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security

1.2 添加 hello 接口

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

1.3 启动项目测试

项目启动成功后,访问/hello 接口会自动跳转到登录页面,这个登录页面是由 Spring Security 提供的。

默认的用户名是 user ,默认的登录密码则在每次启动项目时随机生成 查看项目启动日志,

2、配置用户名和密码

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

spring.security.user.name=wi-gangspring.security.user.password=123456spring.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;@CacheConfigpublic class MyWebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder(){ return NoOpPasswordEncoder.getInstance(); } @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;@Configurationpublic class MyWebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } @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 中添加如下接口进行测试:

@RestControllerpublic 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
的实现即可,代码如下:

@BeanPasswordEncoder 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 就可以实现登录。本案
例使用了配置在内存中的用户,一般情况下,用户信息是存储在数据库中的,因此需要在用户注册时对密码进行加密处理,代码如下:

@Servicepublic 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 进行测试,代码如下:

@Servicepublic 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 角色

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。