Spring Security5、使用JSON处理登录失效

在我们访问接口时常常会遇到登录失效的问题,例如:登录超时、服务器重启这些都会导致登录失效。

Spring Security 在用户登录时效后,会自动跳转到了登录页面去,在前后端分离的情况下,我们希望能像登录授权那样,登录失效也能返回 JSON 字符串。

一、登录失效处理程序

登录失败处理程序和登录授权这些一样,我们只要实现 AuthenticationEntryPoint 这个接口就可以了。

import cn.hutool.core.lang.Console;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.ContentType;
import cn.hutool.json.JSONUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * 未登录或者登录失效
 *
 * @author lixingwu
 */
@Component
public class JsonAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(
            HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException
    ) throws IOException, ServletException {
        Console.log("未登录或者登录失效,{}", authException);
        Dict res = Dict.create()
                .set("code", 1001)
                .set("msg", "未登录或者登录失效")
                .set("data", authException.getMessage());
        ServletUtil.write(response, JSONUtil.toJsonStr(res), ContentType.JSON.toString(CharsetUtil.CHARSET_UTF_8));
    }
}

二、配置登录失效处理程序

我们现在把我们写的 登录失效 处理程序配置到HttpSecurity上,这样在登录失效时就返回我们设置的json字符串了。注意:这个操作会导致默认的登录页面不可使用,完整配置看代码。

import com.miaopasi.securitydemo.config.security.handler.JsonAccessDeniedHandler;
import com.miaopasi.securitydemo.config.security.handler.JsonAuthenticationEntryPoint;
import com.miaopasi.securitydemo.config.security.handler.JsonFailureHandler;
import com.miaopasi.securitydemo.config.security.handler.JsonSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * Security配置类,会覆盖yml配置文件的内容
 *
 * @author lixingwu
 */
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final JsonSuccessHandler successHandler;
    private final JsonFailureHandler failureHandler;
    private final JsonAccessDeniedHandler accessDeniedHandler;
    private final JsonAuthenticationEntryPoint authenticationEntryPoint;

    @Autowired
    public SecurityConfig(JsonSuccessHandler successHandler, JsonFailureHandler failureHandler, JsonAccessDeniedHandler accessDeniedHandler, JsonAuthenticationEntryPoint authenticationEntryPoint) {
        this.successHandler = successHandler;
        this.failureHandler = failureHandler;
        this.accessDeniedHandler = accessDeniedHandler;
        this.authenticationEntryPoint = authenticationEntryPoint;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/code", "/login", "/logout", "/doLogin").permitAll()
                .antMatchers("/admin/**", "/guest/**").hasRole("admin")
                .antMatchers("/guest/**").hasRole("guest")
                .anyRequest().authenticated()
                .and().formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .loginProcessingUrl("/doLogin")
                .successHandler(successHandler)
                .failureHandler(failureHandler)
                .and().exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler)
            
                // 设置登录失效处理程序
                .authenticationEntryPoint(authenticationEntryPoint)

                .and().cors()
                .and().csrf().disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .passwordEncoder(new BCryptPasswordEncoder())
                .withUser("admin")
                .password(new BCryptPasswordEncoder().encode("123456"))
                .roles("admin", "guest")
                .and()
                .withUser("user")
                .password(new BCryptPasswordEncoder().encode("000000"))
                .roles("guest");
    }
}

三、测试

我在 application.yml 中设置session的超时时间为30s,

server:
  servlet:
    session:
      timeout: 30s

然后登录,30s内访问正常访问,超过30s后访问,返回json字符串。

在未登录时,我们直接请求接口 /admin/get/guest/get 返回json字符串。

{
  "msg": "未登录或者登录失效",
  "code": 1001,
  "data": "Full authentication is required to access this resource"
}

spring security系列文章请 点击这里 查看。
这是代码 码云地址
注意注意!!!项目是使用分支的方式来提交每次测试的代码的,请根据章节来我切换分支。

posted @ 2020-07-12 19:16  喵喵扑  阅读(1072)  评论(0编辑  收藏  举报