前面我们简单使用了 Spring Security Spring Security初使用 我们使用的是默认的登录页面,这个登录页面我们是可以自定义的。

接下来我们继续完善前面的 SecurityConfig 类,继续重写它的 configure(WebSecurity web) 和 configure(HttpSecurity http) 方法

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()//定义用户
                .withUser("zhangsan")//用户名
                .password("zhangsan123")//密码
                .roles("admin");//用户角色
        //如果需要配置多个用户,用 and 相连
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        //用来配置忽略掉的 URL 地址,一般对于静态文件,我们可以采用此操作。
        web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()//结束当前标签,上下文回到HttpSecurity,开启新一轮的配置。
                .formLogin()
                .loginPage("/login.html")//登录页
                .permitAll()//登录相关的页面/接口不拦截。
                .and()
                .csrf().disable();//关闭csrf
    }
}

在resources下创建static,然后在static目录下创建登录页面login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>登录</title>
</head>
<body>
<div>
    <form action="/login.html" method="post">
        <div class="input">
            <label for="name">用户名</label>
            <input type="text" name="username" id="name">
        </div>
        <div class="input">
            <label for="pass">密码</label>
            <input type="password" name="password" id="pass">
        </div>
        <div class="button login">
            <button type="submit">登录</button>
        </div>
    </form>
</div>
</body>
</html>

这里没写样式,就简单演示一下
然后启动项目访问http://localhost:8080/hello,可以看到会重定向到http://localhost:8080/login.html
然后输入配置的用户名密码即可登录成功。

在 Spring Security 中,如果我们不做任何配置,默认的登录页面和登录接口的地址都是 /login,
GET 请求表示你想访问登录页面,如果是 POST 请求,表示你想提交登录数据。
当我们配置了 loginPage 为 /login.html 之后,就是设置登录页面和登录接口的地址为 /login.html。
如果想把登录页面和登录接口地址分开可以通过 loginProcessingUrl 方法来指定登录接口地址。

.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/doLogin")
.permitAll()
.and()

然后修改login.html中form标签action值为/doLogin就可以登录成功。

form 表单的相关配置在 FormLoginConfigurer 中,该类继承自 AbstractAuthenticationFilterConfigurer,

在 AbstractAuthenticationFilterConfigurer 的构造方法中,有如下代码:

spring security 修改指定用户的权限_登录页面


这里配置默认的 loginPage 为 /login

FormLoginConfigurer 的初始化方法 init 方法中也调用了父类的 init 方法:

spring security 修改指定用户的权限_springboot_02


而在父类的 init 方法中,又调用了 updateAuthenticationDefaults

spring security 修改指定用户的权限_java_03


可以看到,如果用户没有给 loginProcessingUrl 设置值的话,默认就使用 loginPage 作为 loginProcessingUrl。

所以这也是如果没配置两个配置地址是一样的原因。登录表单中的参数是 username 和 password,我们可以回到 FormLoginConfigurer 类中,在它的构造方法中,我们可以看到有两个配置用户名密码的方法:

spring security 修改指定用户的权限_springsecurity_04


在这里,首先 super 调用了父类的构造方法,传入了 UsernamePasswordAuthenticationFilter 实例,该实例将被赋值给父类的 authFilter 属性。接下来 usernameParameter 方法如下:

spring security 修改指定用户的权限_springsecurity_05


getAuthenticationFilter 实际上是父类的方法,在这个方法中返回了 authFilter 属性,也就是一开始设置的 UsernamePasswordAuthenticationFilter 实例,然后调用该实例的 setUsernameParameter 方法去设置登录用户名的参数:

spring security 修改指定用户的权限_springsecurity_06


当登录请求从浏览器来到服务端之后,我们要从请求的 HttpServletRequest 中取出来用户的登录用户名和登录密码,在 UsernamePasswordAuthenticationFilter 类中,有如下两个方法:

spring security 修改指定用户的权限_springsecurity_07


可以看到,这个时候,就用到默认配置的 username 和 password 了。

这两个参数我们也可以自己配置,配置方式如下:

.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/doLogin")
.usernameParameter("name")
.passwordParameter("passwd")
.permitAll()
.and()

配置完成后,也要修改一下前端页面的name参数名,配置完成后,重启进行登录测试。

登录后我们要进行回调

登录成功回调

登录成功重定向 URL 相关的方法有两个:

  • defaultSuccessUrl
    如果我们在 defaultSuccessUrl 中指定登录成功的跳转页面为 /index,此时分两种情况,如果你是直接在浏览器中输入的登录地址,登录成功后,就直接跳转到 /index,如果你是在浏览器中输入了其他地址,例如 http://localhost:8080/hello,结果因为没有登录,又重定向到登录页面,此时登录成功后,就不会来到 /index ,而是来到 /hello 页面。
    defaultSuccessUrl 还有一个重载的方法,第二个参数如果不设置默认为 false,也就是我们上面的的情况,如果手动设置第二个参数为 true,则 defaultSuccessUrl 的效果和 successForwardUrl 一致。
  • successForwardUrl
    例如 successForwardUrl 指定的地址为 /index ,你在浏览器地址栏输入 http://localhost:8080/hello,结果因为没有登录,重定向到登录页面,当你登录成功之后,就会服务端跳转到 /index 页面;或者你直接就在浏览器输入了登录页面地址,登录成功后也是来到 /index。

相关配置如下:

.and()
.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/doLogin")
.usernameParameter("name")
.passwordParameter("passwd")
.defaultSuccessUrl("/index")
.successForwardUrl("/index")
.permitAll()
.and()

「注意:实际操作中,defaultSuccessUrl 和 successForwardUrl 只需要配置一个即可。」

登录失败回调

与登录成功相似,登录失败也是有两个方法:

  • failureForwardUrl
  • failureUrl
    「这两个方法在设置的时候也是设置一个即可」
    failureForwardUrl 是登录失败之后会发生服务端跳转,failureUrl 则在登录失败之后,会发生重定向。
注销登录

注销登录的默认接口是 /logout

.and()
.logout()
.logoutUrl("/logout")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout","POST"))
.logoutSuccessUrl("/index")
.deleteCookies()
.clearAuthentication(true)
.invalidateHttpSession(true)
.permitAll()
.and()
  • 默认注销的 URL 是 /logout,是一个 GET 请求,我们可以通过 logoutUrl 方法来修改默认的注销 URL。
  • logoutRequestMatcher 方法不仅可以修改注销 URL,还可以修改请求方式,实际项目中,这个方法和 logoutUrl 任意设置一个即可。
  • logoutSuccessUrl 表示注销成功后要跳转的页面。
  • deleteCookies 用来清除 cookie。
  • clearAuthentication 和 invalidateHttpSession 分别表示清除认证信息和使 HttpSession 失效,默认可以不用配置,默认就会清除。

好了,这章到这里就结束了。