前言:
通过实践而发现真理,又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识,又从理性认识而能动地指导革命实践,改造主观世界和客观世界。实践、认识、再实践、再认识,这种形式,循环往复以至无穷,而实践和认识之每一循环的内容,都比较地进到了高一级的程度。
前两期(温故):
Spring Security3.0版本_spring security 3-CSDN博客
Spring Security3.0.1版本-CSDN博客
知新
正片:
在之前的文章,我们发现了一个问题,以理论去指导实践(非自己的理论)是很容易出现bug的
先设计一个环境:
使用SpringBoot Security,用户通过表单进行登陆,校验数据库账号密码
在使用SpringBoot Security框架时,它默认启动了十分多的Security Filter,其中就包括了框架提供的账号密码登录,我们只需要用自己的账号密码进行登录就要修改Security Filter关于账号密码登录的设置
实践开始:
如何修改默认的Security Filter,就需要使用到Security Filter自定义模板
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf(Customizer.withDefaults()).authorizeHttpRequests(authorize -> authorize.anyRequest().authenticated()).httpBasic(Customizer.withDefaults()).formLogin(Customizer.withDefaults());return http.build();}}
复制该内容,粘贴至代码内,如果你看了前两期,你就知道csrf,AuthorizeHttpRequests等http.后面的内容是什么,是一个个Security Filter
Q问:官方做了几个自定义Security Filter
A答:csrf,AuthorizeHttpRequests,HTTP basic,form Login四个自定义Security filter
现在的新的问题出现了,如何找到管理认证登录的Security filter
(点开认证,我们看到了官方提供的一系列内容,其中用户名密码认证登录就是我们需要的)
(读取用户名OR密码,很接近了)
(又分类了,但是我们知道我们的需要,表单)
找到了,如何自定义认证登录
formLogin方法
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain (HttpSecurity http) throws Exception {//自定义认证http.formLogin(element -> element.loginPage("/login")).build();return null;}
}
将login修改为auth登录再看看Security提供的登录页面还能不能登录
会自动转入,好吧不管怎么搞都会跳转到该页面,这条路失败
但是我再重看的时候,发现少写了一句代码
@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain filterChain (HttpSecurity http) throws Exception {//自定义认证http.formLogin(element ->element.loginPage("/auth").permitAll()//少写代码).build();return null;}
}
再次运行
Unsatisfied dependency expressed through method 'setFilterChains' parameter 0: Error creating bean with name 'filterChain' defined in class path resource [author/chen/config/SecurityConfig.class]: Failed to instantiate [org.springframework.security.web.SecurityFilterChain]: Factory method 'filterChain' threw exception with message: permitAll only works with either HttpSecurity.authorizeRequests() or HttpSecurity.authorizeHttpRequests(). Please define one or the other but not both.
报错了,先翻译一下
通过方法 'setFilterChains' 参数 0 表示的不满意的依赖关系:创建在类路径资源中定义的名称为 'filterChain' 的 Bean 时出错 [author/chen/config/SecurityConfig.class]:实例化失败 [org.springframework.security.web.SecurityFilterChain]:工厂方法 'filterChain' 引发异常,并显示消息:permitAll 仅适用于 HttpSecurity.authorizeRequests() 或 HttpSecurity.authorizeHttpRequests()。请定义一个或另一个,但不能同时定义两者。
我懂了,原因是那个多写的代码所以诱导的bug,换句话说permitALL只用于 HttpSecurity.authorizeRequests() 或 HttpSecurity.authorizeHttpRequests()
问题解决了,但是核心问题并没有解决——自定义认证登录
我找到了Security用例,然后看到了登录这两个字,我就顺手点进去看了
看起来,这篇文章涉及到了以下四部分,看下去,貌似无关,然后又换了一个关键词——配置
这不就是我们需要的内容吗!
import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.*;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.web.configuration.*;@Configuration
@EnableWebSecurity
public class WebSecurityConfig {@Beanpublic UserDetailsService userDetailsService() {InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();manager.createUser(User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build());return manager;}
}
没报错,没有引入,说明什么?
CTRL + 鼠标左键 User,我们可以看到
它是security的内部代码
它实现了userDetails接口
看不懂!
放弃,看看对比
共同点:
第一点:实例化InMemoryUserDetailsManager这个类,并调用对象方法createUser
第二点:调用User类,中的withDefaultPasswordEncoder,username,password,roles,build方法
Q:第二张图片就是User
A:其实这就是User的调用,第一张图是将它变成变量去使用
问:是不是只要将User.方法的内容修改为我们数据库的内容就成功了?
我们参考第一张图片,给固定数值,尝试登录看看
我们把密码修改为123456789,启动!
我以为是版本问题,结果看到了这句话,
User.withDefaultPasswordEncoder() is considered unsafe for production and is only intended for sample applications
User.withDefaultPasswordEncoder() 被认为对生产不安全,仅用于示例应用程序
那官方整这一出....
再看源码时
尝试直接调用这个
在直接调用这两个方法时
The method getUsername() in the type User is not applicable for the arguments (String)Java(67108979)
User 类型的方法 getUsername() 不适用于参数 (String)Java(67108979)
我突然发现,这个是对象函数,没办法直接调用。。。。。。。这个不是它的问题是作者的问题
在构建对象时,又发现了
它没有无参数构造函数,需要加入一堆值
然后我根据我的经验,那就是build这个单词在Spring生态里十分的有用,一般在构造开始,或者结束都会加一个
还是报错了
然后在随便点点,点到了这里
还是有withDefault这个方法
找到了通Hello Web Security Java名字一样的代码
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig {final String PASSWORD = "123456789";@Beanpublic UserDetailsService userDetailsService(){UserDetails user = User.withDefaultPasswordEncoder().username("null").password("").build();return new InMemoryUserDetailsManager(user);}
}
还是报错了
总结:
启动了以UserDetailsService类为后续答案为目标,以上实践均为失败
第一点:使用Security filter进行配置,login方法失败,其中permitAll()会产生报错,原因只适用HttpSecurity.authorizeRequests() 或 HttpSecurity.authorizeHttpRequests()方法
第二点:我们找到新实例有两个共同点
第一点:实例化InMemoryUserDetailsManager这个类,并调用对象方法createUser
第二点:调用User类,中的withDefaultPasswordEncoder,username,password,roles,build方法
第三点(基于第二点的实践总结):无法通过添加参数去做到登录