spring security
一、安装spring security
spring boot 提供了 spring-boot-starter-security
启动器,包含了所有于此相关的依赖项目。
最好和优先使用的方法是去用 Spring Initializr,来整合到springboot
- 可以使用IDE[集成开发环境](Eclipse, IntelliJ, NetBeans)。
- 可以去官方网站
- 或者,也可以手动添加
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
由于spring boot使用了maven bom去管理依赖项目,因此可以通过一下方式更改版本号:
<properties>
<!-- ... -->
<spring-security.version>5.2.1.BUILD-SNAPSHOT</spring-security.version>
</properties>
二、简单配置spring security(修改默认表单的值)
spring security配置有3种方式:
- spring Boot Auto Configuration
- Java Configuration
- XML Configuration
1)、spring Boot Auto Configuration
采用spring security的自动配置的话,只会启动spring security的基本的配置。
- spring security的自动配置也就是默认配置,只需要安装完spring security就可以了,启动后访问项目都会被spring security从定向到spring security自动设定的login界面。
- 启动spring security的默认后,spring boot将会将一个叫做springSecurityFilterChain的Filter作为bean放入spring容器,来过滤请求,实施所有的安全处理。
- 安全管理包括网站的所有url,验证提交的用户名和密码,重定向到表单,防范CSRF等
- 网站默认会被重定向一个默认登陆表单,并提供一个账号为user,密码随机的用户
默认下生成用户及相关类的流程:它默认会创建一个名字叫InMemoryUserDetailsManager的实例,它实现了UserDetailsService接口中的loadUserByUsername()方法,并在InitializeUserDetailsBeanManagerConfigurer类中configure()方法中调用新建DaoAuthenticationProvider类并调用setUserDetailsService() 将InMemoryUserDetailsManager的实例放进去
- 查看随机的登录密码
spring security特点(理解自官网):
- 只有经过身份验证的用户才可以和应用程序进行交互(才会处理请求)
- 为你创建一个默认的表单
- 基于表单的认证(通过表单提交验证用户),默认表单用户名为’user’,密码随机
- 用BCrypt(Bcrypt是一个跨平台的文件加密工具。 为了保护用户的明文密码不被泄露,一般会对密码进行单向不可逆加密——哈希 )加密码后存储
- 用户登出
- 阻止CSRF攻击
- 保护会话固定攻击
- http安全相应头
2)、Spring Security Configuration(编程方式)
用写java代码的方式简单配置Spring Security。
1)、将UserDetailsService接口的实现类和PasswordEncoder接口的实现类都存入(注册)到spring容器中
2)、或者将UserDetailsService实现类中loadUserByUsername返回的UserDetails中密码加密,但是必须改成spring security中特有的加密后的格式
第一种:
@Component
@Configuration
public class CoUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String your_username = "user";
if (!username.equals(your_username))
{
throw new UsernameNotFoundException("没有该用户");
}
UserDetails userDetails = new User(your_username , "123456", new ArrayList<GrantedAuthority>());
return userDetails;
}
public static class myPasswordEncoder implements PasswordEncoder
{
@Override
public String encode(CharSequence rawPassword) {
return rawPassword.toString();
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return rawPassword.toString().equals(encodedPassword);
}
}
@Bean
public PasswordEncoder getPe()
{
return new myPasswordEncoder();
}
}
第二种:
加密后格式为{bcrypt}YUyuiHhuh178GYJbjkH…
spring boot可以根据bcrypt得到采用的是何种加密算法,从而将传过来的明文加密然后和系统存储的对比。
以下是spring boot源码:
所有的PasswordEncoder的id:
例子:
@Component
@Configuration
public class CoUserDetailsService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 随便拿spring boot的PasswordEncoder其中之一
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String your_username = "user";
if (!username.equals(your_username))
{
throw new UsernameNotFoundException("没有该用户");
}
String password = "{bcrypt}" + passwordEncoder.encode("123456");
UserDetails userDetails = new User("user", password, new ArrayList<GrantedAuthority>());
return userDetails;
}
}
@Configuration
public class WebSecurityConfig {
// @formatter:off
@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
return new InMemoryUserDetailsManager(user);
//InMemoryUserDetailsManager其实是实现了UserDetailsManager接口,UserDetailsManager接口继承UserDetailsService接口的
}
// @formatter:on
}
简单原理:
-
spring security会根据类型从spring容器中获取UserDetailsService接口的实现类,和PasswordEncoder接口的实现类,然后都放到DaoAuthenticationProvider中登录时由通过它来调用。
-
如果是第一种配置方法(手动注册(注入)了PasswordEncoder到spring容器)
DaoAuthenticationProvider调用UserDetailsService接口下loadUserByUsername方法,验证密码并返回用户信息
DaoAuthenticationProvider调用PasswordEncoder接口下matches方法验证密码
- 如果是二种配置方法(加密密码)
如果没有显式(手动)注册PasswordEncoder的话,将会使用默认的PasswordEncoder,设置默认的PasswordEncoder在构造函数中
返回的是DelegatingPasswordEncoder,与普通的PasswordEncoder不同的是,其encode()函数,也就是加密函数中,用普通PaswordEncoser加密后,在前面加上了所用加密类的ID
这也解释了前面例子中User.withDefaultPasswordEncoder()
为什么可以这样用,
其内部源码
这个函数作用就是:加密后加上所用的哪个PasswordEncoder的id
所以加密后应该是{bcrypt} hJHjkhjk89HuiHGU…这样子的
3)、Spring Security Configuration(xml方式)
<security:authentication-manager>
<security:authentication-provider user-service ref="myUserDetailsService">
</security:authentication-provider>
</security:authentication-manager>
<bean id="myUserDetailsService" class="com/example/demo/configuration/MyUserDetailsService" />
spring boot会默认的直接将UserDetailsService注入到DaoAuthenticationProvider 中,所以,其实和上面的一样