SpringBoot整合Spring Security (二)
一、Test 环境与上一节 的环境一致,这一节主要来进行自定义操作
在 SecurityConfig 中重写这两个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Override public void configure (WebSecurity web) throws Exception { web.ignoring().antMatchers("/js/**" , "/css/**" ,"/images/**" ); } @Override protected void configure (HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin().loginPage("/login.html" ) .permitAll() .and() .csrf().disable() ; }
自行写一个登录页面:
login.html 中的核心代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <form action ="/login.html" method ="post" > <div class ="input" style ="text-align: center" > <label for ="name" > 用户名</label > <input type ="text" name ="username" id ="name" > <span class ="spin" > </span > </div > <div class ="input" style ="text-align: center" > <label for ="pass" > 密码</label > <input type ="password" name ="password" id ="pass" > <span class ="spin" > </span > </div > <div class ="button login" style ="text-align: center" > <button type ="submit" > <span > 登录</span > <i class ="fa fa-check" > </i > </button > </div > </form >
主要注意以下几个方面:
form 的请求地址是 /login.html,方法是 post
用户名和密码的属性值分别是 username 和 password
二、Deep Learning 🐢之疑问一:为啥默认的登录页面和登录接口是一样的?自定义页面又是如何覆盖默认页面的呢?
默认登录页面
关于页面的登录的配置都在:FormLoginConfigurer
其中这个类又是继承 AbstractAuthenticationFilterConfigurer 类的
则在 FormLoginConfigurer 初始化的时候,AbstractAuthenticationFilterConfigurer 的构造方法中:
1 2 3 protected AbstractAuthenticationFilterConfigurer () { setLoginPage("/login" ); }
此时如果没有自定义登录页面,就会是 项目地址/login 这个地址
再看一下 FormLoginConfigurer 中的 init 方法:
1 2 3 4 public void init (H http) throws Exception { super .init(http); initDefaultLoginFilter(http); }
这个方法是调用了父类的 init 方法:
1 2 3 4 5 public void init (B http) throws Exception { updateAuthenticationDefaults(); updateAccessDefaults(http); registerDefaultAuthenticationEntryPoint(http); }
在父类的 init 方法中调用了 updateAuthenticationDefaults() 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 protected final void updateAuthenticationDefaults () { if (loginProcessingUrl == null ) { loginProcessingUrl(loginPage); } if (failureHandler == null ) { failureUrl(loginPage + "?error" ); } final LogoutConfigurer<B> logoutConfigurer = getBuilder().getConfigurer( LogoutConfigurer.class); if (logoutConfigurer != null && !logoutConfigurer.isCustomLogoutSuccess()) { logoutConfigurer.logoutSuccessUrl(loginPage + "?logout" ); } }
此时就能看到:如果我们没有自定义登录接口,那么登录接口的地址就会被设置为和登录页面一致
自定义登录页面
如果自定义了页面地址,首先会到 FormLoginConfigurer 中
1 2 3 public FormLoginConfigurer<H> loginPage (String loginPage) { return super .loginPage(loginPage); }
在这里面调用了父类的 loginPage 方法:
1 2 3 4 5 6 protected T loginPage (String loginPage) { setLoginPage(loginPage); updateAuthenticationDefaults(); this .customLoginPage = true ; return getSelf(); }
可以看到,在这里面 set 完 loginPage 后,又双调用了 updateAuthenticationDefaults() 方法,这个方法在 ☝️ 有
如果自定义了登录接口 loginProcessingUrl ,那么就会进入这个方法:
1 2 3 4 5 6 public T loginProcessingUrl (String loginProcessingUrl) { this .loginProcessingUrl = loginProcessingUrl; authFilter .setRequiresAuthenticationRequestMatcher(createLoginProcessingUrlMatcher(loginProcessingUrl)); return getSelf(); }
🐢之疑问二:在提交的表单中,参数分别为 username 和 password,可不可以自定义呢?
首先还是去 FormLoginConfigurer 类中看看构造方法:
1 2 3 4 5 public FormLoginConfigurer () { super (new UsernamePasswordAuthenticationFilter(), null ); usernameParameter("username" ); passwordParameter("password" ); }
这里的 super 根据 👆 的继承关系,就可以知道是 AbstractAuthenticationFilterConfigurer 中的构造:
1 2 3 4 5 6 7 8 9 protected AbstractAuthenticationFilterConfigurer (F authenticationFilter, String defaultLoginProcessingUrl) { this (); this .authFilter = authenticationFilter; if (defaultLoginProcessingUrl != null ) { loginProcessingUrl(defaultLoginProcessingUrl); } }
注:这里为什么能传参,是由于
UsernamePasswordAuthenticationFilter 继承 AbstractAuthenticationProcessingFilter
且 F extends AbstractAuthenticationProcessingFilter
此时已经有了 UsernamePasswordAuthenticationFilter 的实例了,接着执行 usernameParameter 和 passwordParameter 方法:
1 2 3 4 5 6 7 8 9 public FormLoginConfigurer<H> usernameParameter (String usernameParameter) { getAuthenticationFilter().setUsernameParameter(usernameParameter); return this ; } public FormLoginConfigurer<H> passwordParameter (String passwordParameter) { getAuthenticationFilter().setPasswordParameter(passwordParameter); return this ; }
这里的 getAuthenticationFilter 是父类的方法:
1 2 3 protected final F getAuthenticationFilter () { return authFilter; }
到这里就很明确了,就是将刚才的 new UsernamePasswordAuthenticationFilter() 放到这里面分别调用 setUsernameParameter 和 setPasswordParameter 方法:
1 2 3 4 5 6 7 8 9 public void setUsernameParameter (String usernameParameter) { Assert.hasText(usernameParameter, "Username parameter must not be empty or null" ); this .usernameParameter = usernameParameter; } public void setPasswordParameter (String passwordParameter) { Assert.hasText(passwordParameter, "Password parameter must not be empty or null" ); this .passwordParameter = passwordParameter; }
到这里,usernameParameter 和 passwordParameter 就已经被赋好值了
再通过这个类 (UsernamePasswordAuthenticationFilter) 中的这两个方法从登录请求中拿到这两个值:
1 2 3 4 5 6 7 8 9 @Nullable protected String obtainPassword (HttpServletRequest request) { return request.getParameter(this .passwordParameter); } @Nullable protected String obtainUsername (HttpServletRequest request) { return request.getParameter(this .usernameParameter); }
那么自定义就很简单了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Override protected void configure (HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin().loginPage("/login.html" ) .usernameParameter("user" ) .passwordParameter("pwd" ) .permitAll() .and() .csrf().disable() ; }
🐢之疑问三:登录成功 or 失败后的重定向的地址,注销登录的地址如何自定义?
登录成功
登录失败
defaultSuccessUrl
failureForwardUrl
successForwardUrl
failureUrl
区别简单来说就是:
defaultSuccessUrl 可能你访问的地址是 http://localhost:8080/security 但是你没登录,所以你就跳转到 http://localhost:8080/login 页面下,如果你登录成功,那么就会回到 /security 页面
successForwardUrl 不管是什么页面,登录成功一律是你设置的页面
登录失败同理
注销登录
看下面的代码
logoutUrl:用来注销的地址,默认为 “/logout”
logoutRequestMatcher:改变注销地址,还有请求方式
logoutSuccessUrl:注销登录成功后,要跳转的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Override protected void configure (HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .logout() .logoutUrl("" ) .logoutRequestMatcher(new AntPathRequestMatcher("/logout" ,"POST" )) .logoutSuccessUrl("" ) .and() .formLogin().loginPage("/login.html" ) .usernameParameter("user" ) .passwordParameter("pwd" ) .defaultSuccessUrl("" ) .successForwardUrl("" ) .failureForwardUrl("" ) .failureUrl("" ) .permitAll() .and() .csrf().disable() ; }