Spring Security——基于表单登录认证原理及实现

栏目: Java · 发布时间: 6年前

内容简介:Spring Security——基于表单登录认证原理及实现
spring security默认采用basic模式认证。浏览器发送http报文请求一个受保护的资源,浏览器会
弹出对话框让输入用户名和密码。并以用户名:密码的形式base64加密,加入Http报文头部的Authorization
(默认用户名为user,密码则是会在启动程序时后台console里输出,每次都不一样)。后台获取Http报文头
部相关认证信息,认证成功返回相应内容,失败则继续认证。下面会详细介绍具体认证流程。

二、基于表单的认证原理

基本认证流程:
                     SecurityContextPersistenceFilter
                                   ↓
                   AbstractAuthenticationProcessingFilter
                                   ↓
                    UsernamePasswordAuthenticationFilter
                                   ↓
                         AuthenticationManager
                                   ↓
                         AuthenticationProvider
                                   ↓
                          userDetailsService
                                   ↓
                              userDetails
                                   ↓
                                认证通过
                                   ↓
                            SecurityContext
                                   ↓
                          SecurityContextHolder
                                   ↓
                           RememberMeServices
                                   ↓
                      AuthenticationSuccessHandler
SecurityContextPersistenceFilter会校验请求中session是否有SecurityContext,有放SecurityContextHolder
中,返回时校验SecurityContextHolder中是否有securityContext,有放session,从而实现认证信息在多个请求中共
享。
AbstractAuthenticationProcessingFilter中会调自身attemptAuthentication抽象方法的某个实现
UsernamePasswordAuthenticationFilter便是一个,它从请求中获取账号密码后,构造了一个token,此时没有认证,
如下:
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {
        super((Collection)null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }
随后会调用AuthenticationManager接口中authenticate方法,ProviderManager具体实现,它遍历
provider,调用其supports方法,去匹配之前申明的token,找到provider后调用authenticate方法,
这个方法才开始认证用户信息,表单登录的是DaoAuthenticationProvider其父类进行以下操作:
①调用userDetailsService接口的loadUserByUsername方法获取UserDetails用户信息
②检查UserDetails用户是否可用、是否账户没有过期、是否账户没有被锁定
③检查UserDetails密码是否正确
④检查UserDetails密码是否没有过期
⑤都通过会重新申明一个token,不过多了权限集合参数如下
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,
    Collection authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = credentials;
        super.setAuthenticated(true);
    }
此时用户才被认证通过,上述步骤有一个错了会抛出AuthenticationException中的子异常,下面是其继承图:
Spring Security——基于表单登录认证原理及实现
认证结果返回给AbstractAuthenticationProcessingFilter,将结果放到SecurityContextHolder的
SecurityContext中,若添加了记住我功能又会调用rememberMeServices来实现,最后调用
AuthenticationSuccessHandler接口成功处理。

三、功能实现

在第二部分介绍具体原理,接下来实现自定义登录验证。首先编写一个config类继承WebSecurityConfigurerAdapter
类重写configure方法填写配置
protected void configure(HttpSecurity http) throws Exception {
        http
                /**
                 * 表单登录配置
                 */
                .formLogin() //表单登录
                .loginPage("authentication/login.html") //自定义登录页面
                .loginProcessingUrl("/authentication/form") //与自定义登录页面处理路径一致
//        http.httpBasic()   basic模式弹出框登录
                .successHandler(myAuthenticationSuccessHandler)  //自定义认证成功处理
                .failureHandler(myAuthenticationFailureHandler)  //自定义认证失败处理
                .and()
                /**
                 * 需要认证的请求配置,
                 * 注:最为具体的请求路径放在前面,而最不具体的路径(如anyRequest())放在最后面。
                 * 如果不这样做的话,那不具体的路径配置将会覆盖掉更为具体的路径配置
                 */
                .authorizeRequests()
                //允许这样请求通过,需要将登录所需路径配好,不然会一直重定向
                .antMatchers("/authentication/*").permitAll() 
                .anyRequest().authenticated()//任何请求都需要认证
                .and()
                .csrf().disable(); //关闭csrf防御机制
    }
自定义用户实现UserDetailsService接口重写loadUserByUsername方法
@Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new User(username, "123456", true, true,
                true, true, new ArrayList());
    }
这里的User是security自己的,写死了密码为123456,正常应该根据用户名从数据库查出用户信息。当然密码也不可
能为明文,这里推荐使用BCryptPasswordEncoder加密,因为其每次加密都会生成随机盐加入字符串中。需要在之前申
明的config类添加即可
@Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
这四个boolean值分别表示是否可用、是否账户没有过期、是否密码没有过期、是否账户没有被锁定。构造函数
最后一个参数为该用户用哪些接口权限,同样也从数据库查,但是这个权限只会在登录时初始化一次,若我登录后修
改权限,则无法同步,后续介绍解决办法。
    接下来就是两个登录的自定义登录处理,成功的实现AuthenticationSuccessHandler接口,失败的则实现
AuthenticationFailureHandler接口
@Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException{
        response.setContentType("application/json;UTF-8");
        response.getWriter().println("{\"success\":true,\"result\":\"" + "登录成功" + "\"}");
    }
    
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setContentType("application/json;UTF-8");
        response.getWriter().println("{\"error\":true,\"message\":\"登录名或者密码错误\"}");
    }
注意成功和失败最后的方法参数是不同的,一个是成功的认证信息,另一个失败抛出的认证异常json串的key可以
根据自身登录页面ajax处理结果的属性来定,必须保持一致。
    页面代码就不复制了,做一个简单html就行。

以上所述就是小编给大家介绍的《Spring Security——基于表单登录认证原理及实现》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

如何求解问题

如何求解问题

Zbigniew Michalewicz、David B.Fogel / 曹宏庆 / 中国水利水电出版社 / 2003-2-1 / 35.00元

《如何求解问题:现代启发式方法》通过一系列贯穿于章节间的有趣难题,《如何求解问题:现代启发式方法》深入浅出地阐述了如何利用计算机来求解问题的一些现代启发式方法。全书包括两部分,共分15章。一起来看看 《如何求解问题》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换