在文章开篇先说说为何使用Spring Security,在新的公司中现在认证授权服务是通过Spring Security auth2进行认证的,原本没用过这个啊,只知道apche shiro 现在还有更加厉害的东西,ok啥也别说就是学他。这个系列的文章都是通过自己看视频记得笔记,在这里分享出来,方便大家踩坑。
Spring Security
使用Spring Secruity的原因有很多,单大部分都发现了javaEE的Servlet规范或EJB规范中的安全功能缺乏典型企业应用场景所需的深度。提到这些规范,重要的是要认识到他们在WAR或EAR级别无法移植。因此如果你更换服务器环境,这里有典型的大量工作去重新配置你的应用程序员安全到新的目标环境。使用Spring Security 解决了这些问题,也为你提供许多其他有用的,可定制的安全功能。
Spring Security提供一套的授权功能。这里有三个主要的热点区域,授权web请求、授权方法是否可以被调用和授权访问单个域对象的实例。
spring本质就是一个过滤器链。
demo实例:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kaysanshi</groupId>
<artifactId>demo-security</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-security</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
/**
* Description:
*
* @date:2020/10/23 10:18
* @author: kaysanshi
**/
@RestController
public class SecurityTestController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
@SpringBootApplication
public class DemoSecurityApplication {
public static void main(String[] args) {
SpringApplication.run(DemoSecurityApplication.class, args);
}
}
启动项目后,访问会有一个认证登录界面。这时的密码是在控制台有打印。
这时我就有一个疑问了,那项目启动了,不可能我每次都要去控制态中去弄这个密码啊,那其他服务过来用密码方式进行认证岂不是很麻烦,但是通过慢慢学习还有以下的方式进行配置。分别时代码配置和配置文件配置。
配置用户名和密码
方式一:application.yml配置
server.port=82
## 配置文件配置Spring security 的认证用户名和密码
spring.security.user.name=kay
spring.security.user.password=sanshi
方式二:java代码配置:
/**
* Description:
*
* @date:2020/10/23 10:52
* @author: kaysanshi
**/
//@Configuration
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
// 在配置类中配置认证的密码与用户
auth.inMemoryAuthentication()
.withUser("kay")
.roles("admin")
.password("2a731e08-c7c2-4a44-bc9d-38ada3e824af")
.and()
.withUser("kkk")
.roles("user")
.password("2a731e08-c7c2-4a44-bc9d-38ada3e824af"); // 这里的password 放置加密后的字符串
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
Spring Security 中提供了 BCryptPasswordEncoder 密码编码工具,可以非常方便的实现密码的加密加盐,相同明文加密出来的结果总是不同,这样就不需要用户去额外保存盐
的字段了,这一点比 Shiro 要方便很多。对于BCryptPasswordEncoder这个时什么玩意后面讲,
Spring过滤器链:
包含三个基本过滤器 里面有十几个过滤器:
FileterSecurityInterceptor
是一个方法级别的权限过滤器,基本位于过滤器链的最底部
ExceptionTranslationFilter
是一个异常过滤器,用来处理认证授权过程中抛出的异常
UsernamePasswordAuthenticationFilter
对/login的POST请求拦截,校验表单中用户名密码
SpringSecurity 过滤器加载过程
通过DelegatingFilterProxy
dofilter()–>this.initDelegate()–>this.getTargetName()===(FilterChainProxy) -->this.getFilters()
UserDetailsService接口
当我们不配置账号密码是由Spring Security定义生成的,而在实际项目中账号和密码是通过从数据库中查询出来的。所以我们要自定义逻辑控制认证逻辑。如果需要自定义逻辑则需要实现UserDetailsService接口中的loadUserByUsername方法。
package org.springframework.security.core.userdetails;
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
- 创建一个类继承UsernamePasswordAuthenticationFilter.重写三个方法
- 创建一个类实现UserDetailService编写查询数据库的过程,返回user对象,这个user对象是安全框架提供的对象。
PasswordEncoder接口
在实际的操作中存储密码是加密的方式进行加密。
package org.springframework.security.crypto.password;
public interface PasswordEncoder {
// 表示把参数按照特定的解析规则进行解析
String encode(CharSequence var1);
// 表示验证从储存中获取的编码密码与编码后提交的原始密码是否匹配,如果匹配返回true.(被解析的,储存的密码)
boolean matches(CharSequence var1, String var2);
// 表示如果解析的密码能够再次进行解析且到达更安全的结果,则返回true,否则返回false.默认返回false.
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
BCryptPasswordEncoder是spring security推荐的密码解析器,平时使用最多的解析器。
BCryptPasswordEncoder是对Bcrypt强散列方法的具体实现。是基于hash算法的单向加密,可以同过strength控制机密程度。默认10.
例子:
@Test
void testBCryptPasswordEncoder() {
// 創建密碼解析器
BCryptPasswordEncoder bCryptPasswordEncoder =new BCryptPasswordEncoder();
// 對密碼進行加密
String kay=bCryptPasswordEncoder.encode("kay");
// $2a$10$J8hwGMIPusfpvAlWSTAshORaWk6ZtQq74vu4VRAPIiGR6Vbk1sb3i
System.out.println(kay);
// 判斷原字符串
boolean result = bCryptPasswordEncoder.matches("kay",kay);
System.out.println(result); // true
}
这就是第一篇对Spring Security的认知。