Spring中Websocket身份驗證和授權(quán)的實(shí)現(xiàn)
一、需要了解的事項
- http和WebSocket的安全鏈和安全配置是完全獨(dú)立的。
- SpringAuthenticationProvider根本不參與 Websocket 身份驗證。
- 將要給出的示例中,身份驗證不會發(fā)生在 HTTP 協(xié)商端點(diǎn)上,因為 JavaScript STOMP(websocket)庫不會隨 HTTP 請求一起發(fā)送必要的身份驗證標(biāo)頭。
- 一旦在 CONNECT 請求上設(shè)置,用戶( simpUser) 將被存儲在 websocket 會話中,并且以后的消息將不再需要進(jìn)行身份驗證。
二、依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
</dependency>三、WebSocket 配置
3.1 、簡單的消息代理
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.enableSimpleBroker("/queue/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("stomp");
setAllowedOrigins("*")
}
}3.2 、Spring安全配置
由于 Stomp 協(xié)議依賴于第一個 HTTP 請求,因此需要授權(quán)對 stomp 握手端點(diǎn)的 HTTP 調(diào)用。
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception
http.httpBasic().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/stomp").permitAll()
.anyRequest().denyAll();
}
}然后創(chuàng)建一個負(fù)責(zé)驗證用戶身份的服務(wù)。
@Component
public class WebSocketAuthenticatorService {
public UsernamePasswordAuthenticationToken getAuthenticatedOrFail(final String username, final String password) throws AuthenticationException {
if (username == null || username.trim().isEmpty()) {
throw new AuthenticationCredentialsNotFoundException("Username was null or empty.");
}
if (password == null || password.trim().isEmpty()) {
throw new AuthenticationCredentialsNotFoundException("Password was null or empty.");
}
if (fetchUserFromDb(username, password) == null) {
throw new BadCredentialsException("Bad credentials for user " + username);
}
return new UsernamePasswordAuthenticationToken(
username,
null,
Collections.singleton((GrantedAuthority) () -> "USER") // 必須給至少一個角色
);
}
}接著需要創(chuàng)建一個攔截器,它將設(shè)置“simpUser”標(biāo)頭或在 CONNECT 消息上拋出“AuthenticationException”。
@Component
public class AuthChannelInterceptorAdapter extends ChannelInterceptor {
private static final String USERNAME_HEADER = "login";
private static final String PASSWORD_HEADER = "passcode";
private final WebSocketAuthenticatorService webSocketAuthenticatorService;
@Inject
public AuthChannelInterceptorAdapter(final WebSocketAuthenticatorService webSocketAuthenticatorService) {
this.webSocketAuthenticatorService = webSocketAuthenticatorService;
}
@Override
public Message<?> preSend(final Message<?> message, final MessageChannel channel) throws AuthenticationException {
final StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT == accessor.getCommand()) {
final String username = accessor.getFirstNativeHeader(USERNAME_HEADER);
final String password = accessor.getFirstNativeHeader(PASSWORD_HEADER);
final UsernamePasswordAuthenticationToken user = webSocketAuthenticatorService.getAuthenticatedOrFail(username, password);
accessor.setUser(user);
}
return message;
}
}請注意:preSend() 必須返回 UsernamePasswordAuthenticationToken,Spring 安全鏈中會對此進(jìn)行測試。如果UsernamePasswordAuthenticationToken構(gòu)建沒有通過GrantedAuthority,則身份驗證將失敗,因為沒有授予權(quán)限的構(gòu)造函數(shù)自動設(shè)置authenticated = false 這是一個重要的細(xì)節(jié),在 spring-security 中沒有記錄。
最后再創(chuàng)建兩個類來分別處理授權(quán)和身份驗證。
@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketAuthenticationSecurityConfig extends WebSocketMessageBrokerConfigurer {
@Inject
private AuthChannelInterceptorAdapter authChannelInterceptorAdapter;
@Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
// 這里不用給任何東西
}
@Override
public void configureClientInboundChannel(final ChannelRegistration registration) {
registration.setInterceptors(authChannelInterceptorAdapter);
}
}請注意:這@Order是至關(guān)重要的,它允許我們的攔截器首先在安全鏈中注冊。
@Configuration
public class WebSocketAuthorizationSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
@Override
protected void configureInbound(final MessageSecurityMetadataSourceRegistry messages) {
// 添加自己的映射
messages.anyMessage().authenticated();
}
// 這里請自己按需求修改
@Override
protected boolean sameOriginDisabled() {
return true;
}
}之后編寫客戶端進(jìn)行連接,我們就可以這樣指定客戶端進(jìn)行消息的發(fā)送。
@MessageMapping("/greeting")
public void greetingReturn(@Payload Object ojd){
simpMessagingTemplate.convertAndSendToUser(username,"/topic/greeting",ojd);
}到此這篇關(guān)于Spring中Websocket身份驗證和授權(quán)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Spring Websocket身份驗證和授權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring WebSocket 404錯誤的解決方法
- 完美解決spring websocket自動斷開連接再創(chuàng)建引發(fā)的問題
- SpringBoot集成WebSocket實(shí)現(xiàn)前后端消息互傳的方法
- SpringBoot+WebSocket+Netty實(shí)現(xiàn)消息推送的示例代碼
- SpringBoot+Websocket實(shí)現(xiàn)一個簡單的網(wǎng)頁聊天功能代碼
- springboot websocket簡單入門示例
- SpringMVC整合websocket實(shí)現(xiàn)消息推送及觸發(fā)功能
- Spring集成webSocket頁面訪問404問題的解決方法
- Spring Boot 開發(fā)私有即時通信系統(tǒng)(WebSocket)
- Spring Cloud微服務(wù)使用webSocket的方法
- java中實(shí)現(xiàn)兼容ie6 7 8 9的spring4+websocket
相關(guān)文章
Java后端限制頻繁請求和重復(fù)提交的實(shí)現(xiàn)
很多用戶會請求過于頻繁或者是多次重復(fù)提交數(shù)據(jù),本文主要介紹了Java后端限制頻繁請求和重復(fù)提交的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04
Java獲取客戶端真實(shí)IP地址經(jīng)典寫法詳解
在Web開發(fā)中,獲取客戶端的真實(shí)IP地址是一個常見需求,本文將分別給出經(jīng)典寫法(基于工具類/條件判斷)和Lambda寫法(基于函數(shù)式接口?+?流式處理)的實(shí)現(xiàn),并進(jìn)行對比2026-04-04
Java實(shí)現(xiàn)開箱即用的redis分布式鎖
這篇文章主要為大家詳細(xì)介紹了如何使用Java實(shí)現(xiàn)開箱即用的基于redis的分布式鎖,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,需要的可以收藏一下2022-12-12
Spring啟動時實(shí)現(xiàn)初始化有哪些方式?
今天給大家?guī)淼奈恼率顷P(guān)于Spring的相關(guān)知識,文章圍繞著Spring啟動時實(shí)現(xiàn)初始化有哪些方式展開,文中有非常詳細(xì)的介紹,需要的朋友可以參考下2021-06-06
java rocketmq--消息的產(chǎn)生(普通消息)
這篇文章主要介紹了java rocketmq--消息的產(chǎn)生(普通消息),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,,需要的朋友可以參考下2019-06-06
java后端請求兌現(xiàn)request的中文亂碼問題解決
文章主要講述了在處理處理方案工作中遇到中文亂碼問題的解決過程,通過復(fù)現(xiàn)和分析亂碼問題,發(fā)現(xiàn)是由于解碼規(guī)則和后端服務(wù)編碼不一致導(dǎo)致的,最終通過修改過濾器中的編碼設(shè)置解決了問題2025-02-02

