相关
一、Spring Security是什么?
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的实际标准。
Spring Security 和 Shiro 一样都是安全框架,Shiro是apache的产品,Spring Security 是Spring全家桶的成员。现在SpringBoot是主要潮流,Spring Security 能更好的结合使用。 当然两个产品都有优劣势,具体技术选型还是得看企业各自的应用场景。
二、认证和授权
和Shiro大同小异,都是Authentication(身份认证)和Authorization(授权),简单的来说就是:
- 证明我是谁,我是不是合法用户
- 认证成功后,我能做什么事
1.准备
基于springboot讲解,所以先创建一个springboot项目,然后再在pom.xml里引入web 和 security 的依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
创建一个测试类TestController.java
@RestController
public class TestController {
@RequestMapping(value = "hello", method = RequestMethod.GET)
public String hello() {
return "hello world";
}
}
2.认证
在未引入security的依赖时,访问 http://localhost:8080/hello,正常返回
引入security的依赖,再去访问时,则会跳转到一个登陆表单页面,说明security起作用。
仅仅只是引入依赖,默认的用户名和密码又是多少??
当我们在项目配置文件没有配置属性的时候,默认的用户名是 user 密码则是UUID生成的随机字符串,在项目启动的时候会打印在控制台的日志上。
可以看到密码是由UserDetailsServiceAutoConfiguration
这个类打印的.这个类有一个@Bean 实例了一个InMemoryUserDetailsManager
类,其中一个参数是SecurityProperties
@Bean
@ConditionalOnMissingBean(
type = {"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository"}
)
@Lazy
public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {
User user = properties.getUser();
List<String> roles = user.getRoles();
return new InMemoryUserDetailsManager(new UserDetails[]{org.springframework.security.core.userdetails.User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});
}
private String getOrDeducePassword(User user, PasswordEncoder encoder) {
String password = user.getPassword();
if (user.isPasswordGenerated()) {
logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));
}
return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password;
}
找到security自动配置的配置文件类 SecurityProperties.java
, 可以看到 spring.security的相关配置会被注入进来,
2.1 设置用户名密码
修改默认密码的方式有3种
- 在项目配置文件如
application.properties
里进行配置
# 用户名
spring.security.user.name=
# 密码
spring.security.user.password=
- 通过配置类进行配置,这里新建一个
SecurityConfig.java
继承WebSecurityConfigurerAdapter
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(SecurityConfig.class);
@Override
protected void configure(HttpSecurity http) throws Exception {
log.info("------HttpSecurity http------");
super.configure(http);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
log.info("------AuthenticationManagerBuilder auth------");
super.configure(auth);
}
}
@Configuration
和 @EnableWebSecurity
两个注解让spring security配置生效。
这里两个父类的方法
configure(HttpSecurity http)
:http request的相关配置
- formLogin():表单登陆支持
- httpBasic():配置Http Basic验证
- cors():配置跨域
- sessionManagement():session配置
- authorizeRequests():访问授权配置
- csrf():添加 CSRF 支持
- addFilterBefore():添加过滤器在指定过滤器前
- addFilterAt():添加过滤器替换指定过滤器
- addFilterAfter():添加过滤器在指定过滤器后
- logout():添加退出登录支持
- 等等。。。。
configure(AuthenticationManagerBuilder auth)
:认证相关配置 密码也是在这边配置
代码示例:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
log.info("------AuthenticationManagerBuilder auth------");
auth.inMemoryAuthentication().withUser("admin").password(new BCryptPasswordEncoder().encode("123456"));
}
/**
* 配置security的加密方式
* @return
*/
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
密码加密方式这个是要配置的,否则会报错。
- 自定义实现类,通常实际项目的用户名和密码都是在数据库存储,进行认证需要与数据库交互,与下面的认证流程一起讲解。
2.2 认证流程
在没有自定义实现认证登陆,默认的登陆表单提交调用UsernamePasswordAuthenticationFilter
类的attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
方法
然后调用的是DaoAuthenticationProvider
类
最后UserDetailsService
类返回一个 UserDetails
对象
也就是说我们可以自定义类继承UserDetailsService
然后重写 loadUserByUsername() 方法,获取数据库用户名/密码进行登陆的校验!
实现步骤:
- 创建一个类实现
userDetailsService
重写接口,返回一个user,这里就可以设置用户名/密码
- 在
SecurityConfig
类里面配置UserDetailsService
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
log.info("------AuthenticationManagerBuilder auth------");
auth.userDetailsService(userDetailsService);
}
就是这么简单!
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
// 实际项目,可以在这里与数据库进行交互,未找到用户可以抛出UsernameNotFoundException
// 用户名存在,返回一个user交给security进行密码校验
return null
}
3. 授权
太累了。。。以后再写!!!