系列十一、Spring Security登录接口兼容JSON格式登录

一、Spring Security登录接口兼容JSON格式登录

1.1、概述

前后端分离中,前端和后端的数据交互通常是JSON格式,而Spring Security的登录接口默认支持的是form-data或者x-www-form-urlencoded的,如下所示:

那么如何让Spring Security的登录接口也支持JSON格式登录呢?请看下文分析

1.2、自定义过滤器

java 复制代码
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2024/1/13 9:30
 * @Description:
 */
public class MyUsernamePasswordAuthenticationFilter7007 extends UsernamePasswordAuthenticationFilter {

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (!HttpMethod.POST.name().equals(request.getMethod())) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        }
        String sessionCode = (String) request.getSession().getAttribute("code");
        if (MediaType.APPLICATION_JSON_VALUE.equals(request.getContentType()) || MediaType.APPLICATION_JSON_UTF8_VALUE.equals(request.getContentType())) {
            Map<String, String> loginData = new HashMap<>(16);
            try {
                loginData = new ObjectMapper().readValue(request.getInputStream(), Map.class);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                String paramCode = loginData.get("code");
                try {
                    checkCode(response,paramCode,sessionCode);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            String username = loginData.get(getUsernameParameter());
            String password = loginData.get(getPasswordParameter());
            if (username == null) {
                username = "";
            }
            if (password == null) {
                password = "";
            }
            username = username.trim();
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            setDetails(request,authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        } else {
            try {
                checkCode(response,request.getParameter("code"),sessionCode);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            return super.attemptAuthentication(request, response);
        }
    }

    /**
     * 检查验证码
     * @param response
     * @param paramCode
     * @param sessionCode
     */
    private void checkCode(HttpServletResponse response, String paramCode, String sessionCode) throws Exception {
        if (StringUtils.isBlank(paramCode)) {
            R r = R.error(ResponseEnum.VERIFY_CODE_IS_NULL.getCode(), ResponseEnum.VERIFY_CODE_IS_NULL.getMessage());
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write(new ObjectMapper().writeValueAsString(r));
            out.flush();
            out.close();
            return;
        }
        if (StringUtils.isBlank(sessionCode)) {
            R r = R.error(ResponseEnum.VERIFY_CODE_IS_EXPIRED.getCode(), ResponseEnum.VERIFY_CODE_IS_EXPIRED.getMessage());
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write(new ObjectMapper().writeValueAsString(r));
            out.flush();
            out.close();
            return;
        }
        if (!StringUtils.equals(paramCode.toLowerCase(), sessionCode.toLowerCase())) {
            R r = R.error(ResponseEnum.VERIFY_CODE_IS_NOT_MATCH.getCode(), ResponseEnum.VERIFY_CODE_IS_NOT_MATCH.getMessage());
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write(new ObjectMapper().writeValueAsString(r));
            out.flush();
            out.close();
            return;
        }
    }
}

1.3、配置类

java 复制代码
/**
 * @Author : 一叶浮萍归大海
 * @Date: 2024/1/11 21:50
 * @Description: Spring Security配置类
 */
@Configuration
public class MyWebSecurityConfigurerAdapter7007 extends WebSecurityConfigurerAdapter {

    @Resource
    private MyAuthenticationSuccessHandler7007 successHandler;
    @Resource
    private MyAuthenticationFailureHandler7007 failureHandler;
    @Resource
    private MyLogoutSuccessHandler7007 logoutSuccessHandler;
    @Resource
    private MyAuthenticationEntryPoint7007 authenticationEntryPoint;
    @Resource
    private MyAccessDeniedHandler7007 accessDeniedHandler;
    @Resource
    private UserDetailsService userDetailsServiceImpl;

    /**
     * 密码加密器
     *
     * @return
     */
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    /**
     * 定义基于MyBatis-Plus的用户
     *
     * @return
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsServiceImpl);
    }

    /**
     * 角色继承
     *
     * @return
     */
    @Bean
    protected RoleHierarchy roleHierarchy() {
        RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
        roleHierarchy.setHierarchy("ROLE_admin > ROLE_dba");

        return roleHierarchy;
    }

    @Bean
    public MyUsernamePasswordAuthenticationFilter7007 usernamePasswordAuthenticationFilter() throws Exception {
        MyUsernamePasswordAuthenticationFilter7007 usernamePasswordAuthenticationFilter = new MyUsernamePasswordAuthenticationFilter7007();
        usernamePasswordAuthenticationFilter.setFilterProcessesUrl("/login");
        usernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager());
        // 登录成功回调
        usernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(successHandler);
        // 登录失败回调
        usernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(failureHandler);
        return usernamePasswordAuthenticationFilter;

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/dba/**").hasRole("dba")
                .antMatchers("/admin/**").hasRole("admin")
                .antMatchers("/helloWorld", "/verifyCode/getVerifyCode")
                .permitAll()
                .anyRequest()
                .authenticated()

                .and()

                /**
                 * 注销登录回调
                 */
                .logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(logoutSuccessHandler)
                .permitAll()

                .and()

                .csrf()
                .disable()

                /**
                 * 未认证 & 权限不足回调
                 */
                .exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler);
        http.addFilterAfter(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

}

1.4、测试

相关推荐
xiegwei5 天前
spring security 方法安全@PreAuthorize实现从方法参数中获取数据并判断权限
spring security
TracyCoder1237 天前
SpringSecurity 技术原理深度解析:认证、授权与 Filter 链机制
spring security
泽济天下12 天前
【经验分享】基于Spring Boot 4.0快速实现最简版的OAuth2 Server和Client
spring boot·springboot·oauth2
xiegwei13 天前
spring security oauth2 集成异常处理
数据库·spring·spring security
豆奶特浓618 天前
Java面试生死局:谢飞机遭遇在线教育场景,从JVM、Spring Security到AI Agent,他能飞吗?
java·jvm·微服务·ai·面试·spring security·分布式事务
佛祖让我来巡山19 天前
小明网站双登录系统实现——微信授权登录+用户名密码登录完整指南
oauth2·springsecurity·微信授权登录
努力发光的程序员20 天前
互联网大厂Java面试:从Spring Boot到微服务架构
spring boot·缓存·微服务·消息队列·rabbitmq·spring security·安全框架
佛祖让我来巡山20 天前
小明网站微信登录改造记——OAuth2完整指南(含续期逻辑)
oauth2·微信授权登录
不会吃萝卜的兔子1 个月前
spring - 微服务授权 2 实战
spring·oauth2·authorization
陈果然DeepVersion1 个月前
Java大厂面试真题:从Spring Boot到AI微服务的三轮技术拷问(一)
java·spring boot·redis·微服务·kafka·面试题·oauth2