Spring Security认证配置(三)

学习本章之前,可以先了解下上篇Spring Security认证配置(二)

本篇想要达到这样几个目的:

1、登录成功处理

2、登录失败处理

3、调用方自定义登录后处理类型

 

具体配置代码如下:

 

spring-security-browser

登录成功处理:

/**
 * 自定义登录成功后处理
 */
@Slf4j
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        log.info("登录成功...");

        response.setContentType("application/json;charset=UTF-8");
        // 返回authentication 认证信息
        response.getWriter().write(objectMapper.writeValueAsString(authentication));
    }

}

这段配置表示,登录成功后,会向response中塞入authentication对象

代码解析:

点开 AuthenticationSuccessHandler 类,可以看到里面只有一个onAuthenticationSuccess方法,如下:

onAuthenticationSuccess方法中,有个Authentication对象,其用法可参考AuthenticationManager、ProviderManager

 

登录失败处理:

/**
 * 自定义登录失败后处理
 */
@Slf4j
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {
        log.info("登录失败");

        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); // 服务器内部异常500
        response.setContentType("application/json;charset=UTF-8");
        // 返回异常信息
        response.getWriter().write(objectMapper.writeValueAsString(exception));
    }

}

 这段配置表示,登录失败后,会向response中塞入AuthenticationException 对象,并返回500状态码

代码解析:

AuthenticationFailureHandler 中也只有一个onAuthenticationFailure方法,其入参 AuthenticationException 是认证授权过程中抛出的异常类的基类,

 有以下几个子类:

比较常用的是 BadCredentialsException(密码错误)、UsernameNotFoundException(用户名找不到)等等

 

修改下SecurityConfig类中的配置

  @Autowired
    private SecurityProperty securityProperty;
    
    @Autowired
    private LoginSuccessHandler loginSuccessHandler; //登录成功处理
    
    @Autowired
    private LoginFailureHandler loginFailureHandler; //登录失败处理

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // formLogin()是默认的登录表单页,如果不配置 loginPage(url),则使用 spring security
        // 默认的登录页,如果配置了 loginPage()则使用自定义的登录页
        http.formLogin() // 表单登录
            .loginPage(SecurityConst.AUTH_REQUIRE) 
            .loginProcessingUrl(SecurityConst.AUTH_FORM) // 登录请求拦截的url,也就是form表单提交时指定的action
            .successHandler(loginSuccessHandler)
            .failureHandler(loginFailureHandler)
            .and()
            .authorizeRequests() // 对请求授权
            .antMatchers(SecurityConst.AUTH_REQUIRE, securityProperty.getBrowser().getLoginPage()).permitAll() // 允许所有人访问login.html和自定义的登录页
            .anyRequest() // 任何请求
            .authenticated()// 需要身份认证
            .and()
            .csrf().disable() // 关闭跨站伪造
        ;
    }

 

启动服务访问 http://localhost:18081/index.html,跳转到登录页,输入正确的用户名密码(xwj/1234),跳转到如下页面:

查看控制台,可以看到Authentication对象包含的数据:

清掉浏览器缓存,然后再输入错误的密码,跳转到如下页面:

查看控制台,可以看到AuthenticationException对象包含的数据:

 

上面配置了登录成功和失败的处理,但是只能够返回JSON格式的数据。如果希望返回一个页面或者是跳转,那就可以使用Security默认的处理方式

在Security中,默认的成功跟失败处理分别是 SavedRequestAwareAuthenticationSuccessHandlerSimpleUrlAuthenticationFailureHandler

通过分析 AbstractAuthenticationProcessingFilter 类,可以看到:

在服务启动时,如果在我们的配置类中,没有配置这AuthenticationSuccessHandler跟AuthenticationFailureHandler,则使用这两个默认的。如果配置了,则使用配置中

定义的登录处理类。打个断点可以看到:

 

修改下上面的配置代码:

spring-security-browser

为了达到调用方自定义跳转方式,增加配置类,如下:

/**
 * 登录类型枚举
 */
public enum LoginType {

    REDIRECT,

    JSON

}

修改BrowserProperty,增加默认的登录类型(JSON):

@Getter
@Setter
public class BrowserProperty {

    private String loginPage = "/login.html"; // 登录页
    
    private LoginType loginType = LoginType.JSON; //登录类型

}

接下来修改登录与失败处理类:

/**
 * 自定义登录成功后处理
 * (继承SavedRequestAwareAuthenticationSuccessHandler,这是默认的成功处理器,其带有requestCache)
 */
@Slf4j
@Component
public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private SecurityProperty securityProperty;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        log.info("登录成功...");

        if (LoginType.JSON.equals(securityProperty.getBrowser().getLoginType())) { // json请求(如ajax)
            response.setContentType("application/json;charset=UTF-8");
            // 返回authentication 认证信息
            response.getWriter().write(objectMapper.writeValueAsString(authentication));
        } else { // 跳转
            super.onAuthenticationSuccess(request, response, authentication);
        }
    }

}
/**
 * 自定义登录失败后处理 
 * (继承SimpleUrlAuthenticationFailureHandler,这是默认的失败处理器)
 */
@Slf4j
@Component
public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private SecurityProperty securityProperty;

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {
        log.info("登录失败");

        if (LoginType.JSON.equals(securityProperty.getBrowser().getLoginType())) { // json请求(如ajax)
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); // 服务器内部异常500
            response.setContentType("application/json;charset=UTF-8");
            // 返回异常信息
            response.getWriter().write(objectMapper.writeValueAsString(exception));
        } else {
            super.onAuthenticationFailure(request, response, exception);
        }
    }

}

spring-security-demo

在配置文件(application.yml)中,加上:

security:
  browser: 
    #loginPage: /plogin.html
    loginType: REDIRECT

 

重新启动服务,访问 http://localhost:18081/index.html,跳转到登录页,输入正确的用户名密码(xwj/1234),跳转到具体的页面:

清掉浏览器缓存,然后再输入错误的密码,跳转到如下页面:

从上面可以看到,返回401状态码,类型为Unauthorized,表示未认证。这里的????其实是认证失败原因,打个断点可以看到:

抛出了BadCredentialsException异常,"坏的凭证"表示密码错误

 

posted @ 2018-10-24 09:12  仅此而已-远方  阅读(4464)  评论(0编辑  收藏  举报