首页 > 其他分享 >四、用JSON作前后端分离的交互

四、用JSON作前后端分离的交互

时间:2023-06-06 21:33:39浏览次数:39  
标签:return String exchange 分离 private JSON new 交互 public

在Spring Reactive Security中,Security过滤器是通过类ServerHttpSecurity配置的,用户认证过滤器是AuthenticationWebFilter,相当于SpringSecurity中的UsernamePasswordAuthenticationFilter

 

AuthenticationWebFilter中,用户名和密码的解析是通过 ServerAuthenticationConverter实现的。但是在ServerHttpSecurity的内部类表单配置类FormLoginSpec没有ServerAuthenticationConverter的配置属性:

	private final RedirectServerAuthenticationSuccessHandler defaultSuccessHandler = new RedirectServerAuthenticationSuccessHandler(
			"/");

	private RedirectServerAuthenticationEntryPoint defaultEntryPoint;

	private ReactiveAuthenticationManager authenticationManager;

	private ServerSecurityContextRepository securityContextRepository;

	private ServerAuthenticationEntryPoint authenticationEntryPoint;

	private boolean isEntryPointExplicit;

	private ServerWebExchangeMatcher requiresAuthenticationMatcher;

	private ServerAuthenticationFailureHandler authenticationFailureHandler;

	private ServerAuthenticationSuccessHandler authenticationSuccessHandler = this.defaultSuccessHandler;

	private FormLoginSpec() {
	}

同时在ServerHttpSecurity没有替换过滤器的方法,只有添加过滤器的方法。所以要想解析JSON参数,必须要自定义AuthenticationWebFilter。

 

修改配置类MyReactiveSecurityConfig:

@Configuration
@Slf4j
public class MyReactiveSecurityConfig {
    @Autowired
    private ReactiveUserDetailsService reactiveUserDetailsService;

    @Autowired(required = false)
    private ReactiveUserDetailsPasswordService userDetailsPasswordService;


    private final static String LOGIN_PAGE="/doLogin";

//    @Bean
//    public ReactiveUserDetailsService reactiveUserDetailsService() {
//        UserDetails user = User.withUsername("user")
//                .password("12345")
//                .roles("USER")
//                .build();
//        return new MapReactiveUserDetailsService(user);
//    }

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

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
                .authorizeExchange(exchanges -> exchanges
                        .pathMatchers(LOGIN_PAGE).permitAll()
                        .anyExchange().authenticated()
                )
                .httpBasic().disable()
                .formLogin().disable()
                .csrf().disable();

        http.addFilterAt(authenticationManager(), SecurityWebFiltersOrder.FORM_LOGIN);
        return http.build();
    }


    @Bean
    public ReactiveAuthenticationManager userDetailsRepositoryReactiveAuthenticationManager() {
        UserDetailsRepositoryReactiveAuthenticationManager manager = new UserDetailsRepositoryReactiveAuthenticationManager(this.reactiveUserDetailsService);
        manager.setPasswordEncoder(passwordEncoder());
        manager.setUserDetailsPasswordService(this.userDetailsPasswordService);
        return manager;
    }

    @Bean
    public AuthenticationWebFilter authenticationManager() {
        AuthenticationWebFilter authenticationFilter = new AuthenticationWebFilter(userDetailsRepositoryReactiveAuthenticationManager());
        authenticationFilter.setRequiresAuthenticationMatcher(ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, LOGIN_PAGE));
        authenticationFilter.setAuthenticationFailureHandler((webFilterExchange, exception) -> {
            log.info("认证异常",exception);
            ServerWebExchange exchange = webFilterExchange.getExchange();

            exchange.getResponse().getHeaders().add("Content-type", MediaType.APPLICATION_JSON_UTF8_VALUE);

            Flux<DataBuffer> dataBufferFlux = DataBufferUtils.read(new ByteArrayResource("认证失败".getBytes(StandardCharsets.UTF_8)), exchange.getResponse().bufferFactory(), 1024 * 8);

            return exchange.getResponse().writeAndFlushWith(t -> {
                t.onNext(dataBufferFlux);
                t.onComplete();
            });
        });
        authenticationFilter.setAuthenticationConverter(new JsonServerAuthenticationConverter());
        authenticationFilter.setAuthenticationSuccessHandler((webFilterExchange, authentication) -> {
            log.info("认证成功:{}",authentication);
            ServerWebExchange exchange = webFilterExchange.getExchange();

            exchange.getResponse().getHeaders().add("Content-type", MediaType.APPLICATION_JSON_UTF8_VALUE);

            Flux<DataBuffer> dataBufferFlux = DataBufferUtils.read(new ByteArrayResource("认证成功".getBytes(StandardCharsets.UTF_8)), exchange.getResponse().bufferFactory(), 1024 * 8);

            return exchange.getResponse().writeAndFlushWith(t -> {
                t.onNext(dataBufferFlux);
                t.onComplete();
            });
        });
        authenticationFilter.setSecurityContextRepository(new WebSessionServerSecurityContextRepository());
        return authenticationFilter;
    }
}

配置SecurityWebFilterChain时将httpBasic,formLogin,csrf全部禁止了。禁用csrf是为了演示方便。禁用httpBasic,formLogin这两个是为了只能用JSON格式参数才能登陆。同时要自定义AuthenticationWebFilter。增加的ReactiveAuthenticationManager是为了配置AuthenticationWebFilter的authenticationManagerResolver属性。同时重定义JsonServerAuthenticationConverter解析JSON参数。setAuthenticationFailureHandler处理认证失败的场景。setAuthenticationSuccessHandler处理认证成功的场景。都是返回JSON数据。

 

@Slf4j
public class JsonServerAuthenticationConverter extends ServerFormLoginAuthenticationConverter {

    private ObjectMapper objectMapper = new ObjectMapper();

    private String usernameParameter = "username";

    private String passwordParameter = "password";


    public JsonServerAuthenticationConverter() {

    }

    public JsonServerAuthenticationConverter(String usernameParameter,String passwordParameter) {
        this.usernameParameter = usernameParameter;
        this.passwordParameter = passwordParameter;
    }

    public Mono<Authentication> apply(ServerWebExchange exchange) {
        Flux<DataBuffer> body = exchange.getRequest().getBody();

        return body.map(t -> {
            String s = t.toString(StandardCharsets.UTF_8);
            log.info("参数:{}",s);
            Map<String , Object> map = null;
            try {
                map = objectMapper.readValue(s, Map.class);
            } catch (JsonProcessingException e) {
               log.info("解析JSON参数异常",e);
               map = null;
            }

            if (map != null && !map.isEmpty()) {
               String username = (String) map.get(usernameParameter);
               String password = (String) map.get(passwordParameter);

                Authentication unauthenticated = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
                return unauthenticated;
            }

            return null;
        }).next();
    }
}

从请求中获取JSON数据,解析成Map,根据用户名参数和密码参数构造Authentication。最后的next是为了将Flux转成Mono。因为返回值是Mono类型。ServerFormLoginAuthenticationConverter是org.springframework.security.web.server.authentication.ServerFormLoginAuthenticationConverter

Authentication unauthenticated = UsernamePasswordAuthenticationToken.unauthenticated(username, password);

这个是必须的。不然在我的IDEA上一直提示类型不同。

 

用Postman演示:
 

 

在访问其他接口:
 

 

在演示认证失败:
 

 

还可以试下用浏览器是否可以登录。

标签:return,String,exchange,分离,private,JSON,new,交互,public
From: https://www.cnblogs.com/shigongp/p/17461680.html

相关文章

  • jquery.serializejson.min.js的妙用
    jquery.serializejson.min.js的妙用关于这个jquery.serializejson.min.js插件来看,他是转json的一个非常简单好用的插件。前端在处理含有大量数据提交的表单时,除了使用Form直接提交刷新页面之外,经常碰到的需求是收集表单信息成数据对象,Ajax提交。而在处理复杂的表单时,......
  • 摹客RP丨强大交互功能,真实产品体验一步到位!
    动态交互效果是原型项目的灵魂,也是原型与设计产出物的关键区别。一个好的交互设置不仅能准确地传达静态界面难以表达的逻辑、流程和细节体验,还能呈现出丰富精彩的视觉效果,提高项目的质量。对于产品经理来说,学会交互设计是必不可少的技能。在原型设计工具中,交互功能的重要性不言而喻......
  • tscconfig.json--ts配置文件
    {//include用来指定那些需要被编译的ts文件//exclude用来指定那些不需要被编译的ts目录//默认不被编译的:["node_modules","bower_components","jspm_packages"]"include":[/***表示:任意目录*表示:任意文件*/"./src/**/*"],......
  • 如何在Python中使用JSON模块
    JSON(JavaScriptObjectNotation)是一种流行的轻量级数据交换标准。它表示由键值对组成的数据结构,非常简单易懂。JSON已成为在线服务之间数据交换的行业标准。它广泛用于现代编程语言,包括Python。JSON数据经常表示为嵌套字典、列表和标量值,例如文本、数字、布尔值和空值。之所......
  • 41.QT-多线程与界面之间交互总结
    1.线程与界面组件需要注意的地方在QThread线程中不能直接创建QWidget之类的界面组件.因为在QT中,所有界面组件相关的操作都必须在主线程中(也就是GUIthread)所以,QThread线程不能直接操作界面组件.2.QThread线程如何操作界面组件-方法1将多线程类对象封装为GUI界面类的类成员然......
  • vue之三种与后端交互的方式
    目录一、vue与后端交互之Ajax情况一:出现了跨域问题前端:index.html后端:main.py情况二:解决了跨域问题前端:index.html二、vue与后端交互之fetch简介fetch介绍后端:main.py前端:index.html三、vue与后端交互之axios1.简介html前端一、vue与后端交互之Ajax情况一:出现了跨域问题前端:in......
  • Json解析字符串报错syntax error, expect {, actual string, pos 0, fastjson-version
    ExpectedBEGIN_OBJECTbutwasSTRINGatline1column2path$syntaxerror,expect{,actualstring,pos0,fastjson-version1.2.62syntaxerror,expect{,actualstring,pos0,fastjson-version1.2.62以上的报错都是Json字符串格式错误,比如缺少{},比如两头多了......
  • Vue——表单控制、购物车案例、v-model进阶、与后端交互三种方式、箭头函数
    表单控制//1checkbox 单选 多选//2radio 单选<body><divid="app"><h1>checkbox单选</h1><p>用户名:<inputtype="text"v-model="username"></p><p>密码:<inputtype="p......
  • 表单控制,购物车案例,v-model进阶,与后端交互的三种方式
    1表单控制#1checkebox: -单选-多选#2radio -单选<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><scriptsrc="./js/vue.js"></scrip......
  • 前后端分离架构下使用 Sa-Token 完成登录认证
    一、架构分析目前绝大多数系统都已经采用“前后端分离”架构来设计了,传统的Session模式鉴权也不再适合这种架构(或者需要额外写很多的代码来专门适配)。Sa-Token是一个java轻量级权限认证框架,专为前后端分离架构打造,主要解决登录认证、权限认证、单点登录、OAuth2、微服务网......