WebSocket和SpringSecurity的学习记录

本文详细介绍了WebSocket在SpringBoot中的应用,包括其基本概念、与HTTP的区别、在SpringBoot中配置WebSocket并实现客户端连接,以及如何在SpringSecurity框架下进行安全登录和密码管理。
摘要由CSDN通过智能技术生成

1. WebSocket 基础概念

什么是 WebSocket?
  • WebSocket 是一种网络通信协议,提供了在单个 TCP 连接上进行全双工通信的方式。"全双工"意味着客户端和服务器可以同时发送和接收信息,这与传统的 HTTP 请求不同,后者是一种半双工通信方式(即一次只能进行一个请求或响应)。
WebSocket 与 HTTP 的区别
  • 连接持续性:HTTP 协议是基于请求-响应模式的,通常在请求被服务端响应后连接就会关闭。而 WebSocket 在握手后就建立了一个持久的连接,直到客户端或服务器决定关闭这个连接。
  • 实时性:WebSocket 支持实时数据传输,这对于需要快速响应的应用(如在线游戏、实时聊天等)来说非常重要。
  • 头信息大小:WebSocket 的头信息比 HTTP 小,这意味着在数据传输时,WebSocket 的开销更小。
建立连接的过程
  1. 客户端请求:WebSocket 连接是通过 HTTP 请求“升级”来初始化的。客户端发送一个特殊的 HTTP 请求,包含 Upgrade: websocketConnection: Upgrade 头信息。
  2. 服务器响应:如果服务器支持 WebSocket,则会以一个状态码 101 Switching Protocols 响应,表示服务器同意改变协议。
  3. 数据传输:一旦握手完成,数据就可以通过这个连接双向传输。
使用场景
  • 实时聊天应用
  • 在线游戏
  • 实时数据更新(如股票报价)
  • 协作应用(如实时文档编辑)
WebSocket URL
  • WebSocket URL 以 ws:// (非加密) 或 wss:// (加密) 开头,类似于 HTTP 的 http:// 和 HTTPS 的 https://

2. 在 Spring Boot 中使用 WebSocket

步骤 1: 添加依赖项

首先,在你的 pom.xml 文件中添加 Spring Boot 对 WebSocket 的支持。这通常意味着添加 spring-boot-starter-websocket 依赖。

xmlCopy code<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    <!-- 其他依赖 -->
</dependencies>
步骤 2: 配置 WebSocket

在 Spring Boot 应用中创建一个配置类来启用和配置 WebSocket。


@Configuration
@EnableWebSocketMessageBroker  // 启用 WebSocket 消息代理
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 注册一个 WebSocket 端点,客户端将使用它连接到 WebSocket 服务器。
        registry.addEndpoint("/ws").withSockJS();
        // SockJS 用于兼容不支持 WebSocket 的浏览器
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        // 定义消息代理,用于将消息从一个客户端路由到另一个客户端
        registry.setApplicationDestinationPrefixes("/app");
        // 客户端将使用此前缀发送消息
        registry.enableSimpleBroker("/topic");
        // 定义了一个简单的消息代理,客户端可以订阅的前缀
    }
}

这里使用了 STOMP(Simple Text Oriented Messaging Protocol)作为 WebSocket 的子协议。STOMP 提供了一个可互操作的线路格式,允许 STOMP 客户端与任何支持 STOMP 的消息代理进行交互。

步骤 3: 创建 WebSocket 控制器

接下来,创建一个控制器来处理发送到 WebSocket 的消息。

@Controller
public class WebSocketController {

    @MessageMapping("/hello")  // 定义消息的地址
    @SendTo("/topic/greetings")  // 定义客户端订阅的地址
    public String greeting(String message) throws Exception {
        // 当服务器接收到 "/app/hello" 的消息时,它会调用此方法
        return "Hello, " + message + "!";
    }
}

这个控制器将接收发送到 /app/hello 的消息,并将响应发送到 /topic/greetings

步骤 4: 客户端连接

在客户端,你需要使用一个合适的库来建立 WebSocket 连接。如果你的客户端是一个网页,你可以使用 SockJS 和 STOMP 客户端库。

<!DOCTYPE html>
<html>
<head>
    <title>WebSocket Test</title>
    <script src="https://cdn.jsdelivr.net/npm/sockjs-client/dist/sockjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/stomp-websocket/lib/stomp.min.js"></script>
</head>
<body>
    <script type="text/javascript">
        var socket = new SockJS('/ws'); // 连接到服务器上的 WebSocket 端点
        var stompClient = Stomp.over(socket); // 使用 STOMP 子协议

        stompClient.connect({}, function (frame) {
            // 成功连接后的回调函数
            console.log('Connected: ' + frame);
            stompClient.subscribe('/topic/greetings', function (greeting) {
                // 订阅 '/topic/greetings' 以接收消息
                alert(JSON.parse(greeting.body).content);
            });
        });

        function sendMessage() {
            stompClient.send("/app/hello", {}, JSON.stringify({'name': "yourName"}));
            // 向 '/app/hello' 发送消息
        }
    </script>
    <button onclick="sendMessage()">Send Message</button>
</body>
</html>

2.Spring Security安全框架

Spring Security 是一个专门用于 Java 应用程序的安全框架。它为基于 Spring 的应用程序提供了全面的安全解决方案。

security提供了一个默认的登录页面,在没有登录的情况下所有的请求都会被拦截到该页面,默认的登录名和密码是可以改的在yml配置文件中

spring:
  security:
    user:
      name: boss
      password: 123

使用顺序

  • 创建数据库
  • 使用Mybatis管理dao层
  • 创建密码解析器——PasswordEncoder
public class MyPasswordEncode implements PasswordEncoder {
    /*
    * 密码加密方法
    * */
    @Override
    public String encode(CharSequence rawPassword) {
        System.out.println("自定义密码解析器,encode方法执行");
        return rawPassword.toString();
    }

    /**
     * 校验密码明文和密文是否相同的方法
     * @param rawPassword 明文密码,客户端传递的
     * @param encodedPassword 密文密码,服务器中存储的
     * @return
     */
    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {

//        先使用encode方法,用相同的加密策略,加密明文,在对比密文
        return encode(rawPassword).equals(encodedPassword);
    }
    /*
     * 是否需要升级密码解析策略,强化密码解析策略
     * */
    @Override
    public boolean upgradeEncoding(String encodedPassword) {
        return PasswordEncoder.super.upgradeEncoding(encodedPassword);
    }
}

创建配置类管理该解析器——解析器唯一

在这里插入图片描述

  • 定义登录服务类型——方法的实现内容是:根据用户名查询用户对象,和用户权限列表。(MyUserDetailsServiceImpl)

在这里插入图片描述

/**
 * 登录服务实现类型
 */
@Component
public class MyUserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    userMapper userMapper;
    /**
     * 方法的实现内容是:根据用户名查询用户对象,和用户权限列表。
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//      根据用户名查询用户
        User user =userMapper.SelectByUserName(username);
//        判断用户是否存在
        if (user==null)
        {
            throw new UsernameNotFoundException("用户名或者密码错误");
        }
//        返回UserDetails接口类型对象
        org.springframework.security.core.userdetails.User result=
                new org.springframework.security.core.userdetails.User(
                        username,//登录用户的用户名
                        user.getPassword(),//登录用户的密码,是服务器保存的密文
                        AuthorityUtils.NO_AUTHORITIES//工具提供的无权限空集合,
                        );
        return result;
    }
}


sercurity**默认设置的登录自动自定义验证**执行顺序

  1. 在默认的页面登录的时候,sercurity会自动调用实现PosswordEndcoder接口的类中的encode方法,进行密码的解析
  2. 然后sercurity接着调用实现UserDetailsService接口的实现类中的loadUserByUsername方法,进行例如用户的查询
  3. 然后sercurity会接着调用实现PosswordEndcoder接口的类中的encode方法,和matches进行库密码和表单密码的校验

在这里插入图片描述

MySecurityConfiguration配置类的编写

@Configuration
@EnableWebSecurity
public class MySecurityConfiguration {
    /**
     * 创建Security过滤器链对象
     * @return
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity security) throws Exception {
//        登录配置类
        Customizer<FormLoginConfigurer<HttpSecurity>> customizer = new Customizer<FormLoginConfigurer<HttpSecurity>>(){
            @Override
            public void customize(FormLoginConfigurer<HttpSecurity> configurer) {
                //具体的认证配置
                configurer
                        .loginPage("/login")
//                        .defaultSuccessUrl("/test")//默认跳转地址
//                        .successForwardUrl("/test") //认证成功后跳转地址
                        .successHandler(new MyAuthenticationsuccessHandler("/test",true))//设置认证成功后的处理代码逻辑。
                        .failureUrl("/loginfail")//登录失败跳转地址
                ;//设置登录页面访问地址。默认是/login。必须是get请求。自定义后提供控制器+视图
            }
        };
//        设置认证配置
    security
            .formLogin(customizer);

//    退出登录配置
        Customizer<LogoutConfigurer<HttpSecurity>> logoutCustomizer = new Customizer<LogoutConfigurer<HttpSecurity>>() {
            @Override
            public void customize(LogoutConfigurer<HttpSecurity> configLoginout) {
                configLoginout
                        .logoutUrl("/logout")//退出登录请求地址
                        .logoutSuccessUrl("/login")//退出登录成功以后跳转地址,默认是登录页面地址?logou
                        .logoutSuccessHandler(new MyLogOutsuccessHandler())//设置退出登录成功后的处理代码
                        .addLogoutHandler(new MyLogOutHandler())
                ;
            }
        };
//        设置登出配置
        security.logout(logoutCustomizer);

    security
            .authorizeRequests()
            .antMatchers("/login","/css/**","/image/**","/loginfail").permitAll() // 允许所有用户访问 /login 路径
            .anyRequest().authenticated(); // 所有其他请求都需要认证
//        关闭CSRF功能
        security.csrf().disable();
        return security.build();
    }
    /**
     * 创建一个passwordencoder类型bean对象
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        //强散列密码解析器,构造方法可以传递整型参数,范围在4~31之间,数字越大强度越高,性能越低
        return new BCryptPasswordEncoder();
    }

}

MyAuthenticationsuccessHandler类用于处理认证成功的处理逻辑

//认证成功后的处理逻辑
public class MyAuthenticationsuccessHandler implements AuthenticationSuccessHandler {
//  登录成功后访问的地址
    private String url;
    // 是否是重定向
    private boolean isRedirect;
    public MyAuthenticationsuccessHandler( String url,boolean isRedirect){
            this.url=url;
            this.isRedirect=isRedirect;
    }
    /**
     * 认证成功后具体的执行代码
     * @param request
     * @param response
     * @param authentication  认证成功后的用户主体对象,包含个人登录的信息和权限列表
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        if(isRedirect){
            //重定向
            response.sendRedirect(url);
        }else
            //请求转发
            request.getRequestDispatcher(url).forward(request,response);
    }
}

MyLogOutHandler(登出的一般做额外处理,比如保存会话中某些attribute到数据库)

public class MyLogOutHandler implements LogoutHandler {
    /**
     * 处理时逻辑
     * @param request
     * @param response
     * @param authentication
     */
    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        //一般做额外处理,比如保存会话中某些attribute到数据库
        //比如记录日志
    }
}

MyLogOutsuccessHandler(登出成功的处理逻辑)

public class MyLogOutsuccessHandler implements LogoutSuccessHandler {
    /**
     * 退出登录成功后的处理方案
     */
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println("退出登录成功后的处理");
        //销毁会话,清空缓存
      request.getSession().invalidate();//销毁会话
        authentication.setAuthenticated(false);//设置未登录状态
        response.sendRedirect("/login");//进行重定向
    }
}

  • 18
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要在nginx中放行websocket,你需要在nginx的配置文件中添加相应的配置。根据引用\[1\]和引用\[2\]的内容,你可以使用以下配置来放行websocket连接: ``` location /websocket/ { proxy_pass http://127.0.0.1:8080; //你自己的转发目标地址 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_read_timeout 36000s; //设置超时时间 } ``` 或者使用以下配置: ``` location /way/ { proxy_pass http://localhost:8913; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 3600s; //设置超时时间 } ``` 这样配置后,nginx会将websocket的请求转发到指定的目标地址,并且设置了相应的请求头信息和超时时间,从而实现了对websocket放行。 #### 引用[.reference_title] - *1* [学习记录680@springboot+vue+nginx+springsecurity环境下的websocket实战](https://blog.csdn.net/weixin_44663675/article/details/130245139)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [springzuul+spring security+https连接下使用websocket总结](https://blog.csdn.net/weixin_43900374/article/details/110917217)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weighless1129

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值