摘要:最后發(fā)現(xiàn)每一個(gè)在連接端點(diǎn)之前都會(huì)發(fā)送一個(gè)請(qǐng)求用于保證該服務(wù)是存在的。而該請(qǐng)求是程序自動(dòng)發(fā)送的自能自動(dòng)攜帶數(shù)據(jù),無(wú)法發(fā)送自定義。問(wèn)題是我們的是使用自定義實(shí)現(xiàn)的認(rèn)證。所以該方法不成立,所以只能讓自己認(rèn)證。
使用框架介紹
spring boot 1.4.3.RELEASE
spring websocket 4.3.5.RELEASE
spring security 4.1.3.RELEASE
sockjs-client 1.0.2
stompjs 2.3.3
項(xiàng)目介紹由于公司需要使用websocket主動(dòng)給前端用戶(hù)推送消息,公司的項(xiàng)目是使用jhipster自動(dòng)生成的微服務(wù)項(xiàng)目,而spring boot本身就集成了websocket,這樣我們不用自己處理所有的網(wǎng)絡(luò)細(xì)節(jié)代碼。我們的項(xiàng)目主要為:
前端 - nodeJS代理 - 后端 - 計(jì)算系統(tǒng)(由于我們公司是做云計(jì)算的,計(jì)算系統(tǒng)是一個(gè)底層系統(tǒng))
項(xiàng)目的主要流程是:
由于我們使用的是spring security oauth2 來(lái)進(jìn)行認(rèn)證,而且我們需要吧websocket消息推送給指定用戶(hù),這樣為了保證websocket和http協(xié)議使用的同一套認(rèn)證系統(tǒng),我們就必須要把websocket認(rèn)證集成到spring security中。
第一個(gè)問(wèn)題認(rèn)證403錯(cuò)誤首先貼出websocket的配置代碼
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { private final static Logger LOG = LoggerFactory.getLogger(WebSocketConfig.class); @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/api/v1/socket/send"); // 推送消息前綴 registry.setApplicationDestinationPrefixes("/api/v1/socket/req"); // 應(yīng)用請(qǐng)求前綴 registry.setUserDestinationPrefix("/user");//推送用戶(hù)前綴 } /** * 建立連接的端點(diǎn) * @param registry */ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/api/v1/socket/fallback").setAllowedOrigins("*").withSockJS().setInterceptors(httpSessionHandshakeInterceptor()); } }
第一次在打開(kāi)websocket的時(shí)候發(fā)現(xiàn)三次握手都沒(méi)有出現(xiàn)就直接報(bào)403,最后仔細(xì)看錯(cuò)誤信息發(fā)現(xiàn)錯(cuò)誤信息的鏈接為:/api/v1/socket/fallback/info,而且該請(qǐng)求使用的是http請(qǐng)求不是websocket請(qǐng)求。最后發(fā)現(xiàn)每一個(gè)websocket在連接端點(diǎn)之前都會(huì)發(fā)送一個(gè)http GET請(qǐng)求用于保證該服務(wù)是存在的。而該請(qǐng)求是程序自動(dòng)發(fā)送的自能自動(dòng)攜帶cookie數(shù)據(jù),無(wú)法發(fā)送自定義header。
spring boot自帶的認(rèn)證是:如果/api/v1/socket/fallback/info該請(qǐng)求通過(guò)認(rèn)證,那么websocket的所有請(qǐng)求以及發(fā)送全部自動(dòng)綁定該認(rèn)證用戶(hù)。如果我們想辦法讓/api/v1/socket/fallback/info請(qǐng)求通過(guò)認(rèn)證,那么接下來(lái)所有的問(wèn)題都將解決。問(wèn)題是我們的token是使用自定義header實(shí)現(xiàn)的認(rèn)證。所以該方法不成立,所以只能讓websocket自己認(rèn)證。
為了解決/api/v1/socket/fallback/info請(qǐng)求的403問(wèn)題我在安全配置中加入.authorizeRequests().antMatchers("/api/v1/socket/fallback/**").permitAll()這樣第一步判斷服務(wù)是否存在就解決了,這里離解決websocket的認(rèn)證問(wèn)題只是第一步。
第二個(gè)問(wèn)題:如果發(fā)送token給后端stomp 客戶(hù)端可以直接在websocket請(qǐng)求中加入自定義header,如下:
let socket = new SockJS("/api/v1/socket/fallback") let stompClient = Stomp.over(socket) let token = localStorage.getItem("Auth-Token") // eslint-disable-line stompClient.connect({"Auth-Token": token}, frame => { stompClient.subscribe("/user/api/v1/socket/send/greetings", data => { // TODO }) })第三個(gè)問(wèn)題:后端如何認(rèn)證
我們?cè)趧?chuàng)建連接的時(shí)候前端需要將token發(fā)送到后端,現(xiàn)在我們已經(jīng)將token發(fā)送到后端了,但是后端如何接受并處理token得到認(rèn)證數(shù)據(jù)呢?帶著這個(gè)問(wèn)題開(kāi)始google吧!http://stackoverflow.com/questions/39422053/spring-4-x-token-based-websocket-sockjs-fallback-authentication這個(gè)鏈接正好解決了我的問(wèn)題,
UPDATE 2016-12-13 : the issue referenced below is now marked fixed, so the hack below is no longer necessary which Spring 4.3.5 or above. See https://github.com/spring-projects/spring-framework/blob/master/src/asciidoc/web-websocket.adoc#token-based-authentication.
原來(lái)這個(gè)問(wèn)題在4.3.5版本中已經(jīng)被繼承進(jìn)去了,查看自己的版本是4.3.4,不解釋直接升級(jí)版本到4.3.5,然后加如代碼
@EnableWebSocketMessageBroker public class MyConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.setInterceptors(new ChannelInterceptorAdapter() { @Override public Message> preSend(Message> message, MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); if (StompCommand.CONNECT.equals(accessor.getCommand())) { String jwtToken = accessor.getFirstNativeHeader("Auth-Token"); if (StringUtils.isNotEmpty(jwtToken)) { UserAuthenticationToken authToken = tokenService.retrieveUserAuthToken(jwtToken); SecurityContextHolder.getContext().setAuthentication(authToken); accessor.setUser(authToken); } } return message; } }); } }
開(kāi)始測(cè)試,發(fā)現(xiàn)還是報(bào)錯(cuò)MissingCsrfTokenException,然后開(kāi)始debug代碼,發(fā)現(xiàn)代碼錯(cuò)誤的代碼為:
public final class CsrfChannelInterceptor extends ChannelInterceptorAdapter { private final MessageMatcher
仔細(xì)查看里面的數(shù)據(jù),原來(lái)這里是需要在header中存放一些數(shù)據(jù),于是乎將configureClientInboundChannel方法修正為:
@Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.setInterceptors(new ChannelInterceptorAdapter() { @Override public Message> preSend(Message> message, MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); if (StompCommand.CONNECT.equals(accessor.getCommand())) { String jwtToken = accessor.getFirstNativeHeader("Auth-Token"); LOG.debug("webSocket token is {}", jwtToken); if (StringUtils.isNotEmpty(jwtToken)) { Map sessionAttributes = SimpMessageHeaderAccessor.getSessionAttributes(message.getHeaders()); sessionAttributes.put(CsrfToken.class.getName(), new DefaultCsrfToken("Auth-Token", "Auth-Token", jwtToken)); UserAuthenticationToken authToken = tokenService.retrieveUserAuthToken(jwtToken); SecurityContextHolder.getContext().setAuthentication(authToken); accessor.setUser(authToken); } } return message; } }); }
然后修改websocket安全配置為:
@Configuration public class WebsocketSecurityConfiguration extends AbstractSecurityWebSocketMessageBrokerConfigurer { @Override protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) { messages.anyMessage().permitAll(); } @Override protected boolean sameOriginDisabled() { return true; } }
這樣websocket 集成spring boot token的認(rèn)證就搞定了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/66357.html
摘要:認(rèn)證服務(wù)器和瀏覽器控制臺(tái)也沒(méi)有報(bào)錯(cuò)信息。這里簡(jiǎn)單介紹下如何查閱源碼,首先全局搜索自己的配置因?yàn)檫@個(gè)地址是認(rèn)證服務(wù)器請(qǐng)求授權(quán)的,所以,請(qǐng)求認(rèn)證的過(guò)濾器肯定包含他。未完待續(xù),下一篇介紹資源服務(wù)器和認(rèn)證服務(wù)器的集成。 基于spring-security-oauth2-實(shí)現(xiàn)單點(diǎn)登錄 文章代碼地址:鏈接描述可以下載直接運(yùn)行,基于springboot2.1.5,springcloud Green...
摘要:前言現(xiàn)在的好多項(xiàng)目都是基于移動(dòng)端以及前后端分離的項(xiàng)目,之前基于的前后端放到一起的項(xiàng)目已經(jīng)慢慢失寵并淡出我們視線,尤其是當(dāng)基于的微服務(wù)架構(gòu)以及單頁(yè)面應(yīng)用流行起來(lái)后,情況更甚。使用生成是什么請(qǐng)自行百度。 1、前言 現(xiàn)在的好多項(xiàng)目都是基于APP移動(dòng)端以及前后端分離的項(xiàng)目,之前基于Session的前后端放到一起的項(xiàng)目已經(jīng)慢慢失寵并淡出我們視線,尤其是當(dāng)基于SpringCloud的微服務(wù)架構(gòu)以及...
摘要:對(duì)于每個(gè)案例,我們插入所需要的測(cè)試數(shù)據(jù),調(diào)用需要測(cè)試的函數(shù)并對(duì)結(jié)果作出斷言。我們將這個(gè)套接字和用戶(hù)返回以供我們其他的測(cè)試使用。 原文地址:Elixir, Phoenix, Absinthe, GraphQL, React, and Apollo: an absurdly deep dive - Part 2 原文作者:Zach Schneider 譯文出自:掘金翻譯計(jì)劃 本文永久鏈接:gi...
摘要:在整個(gè)學(xué)習(xí)過(guò)程中,我最關(guān)心的內(nèi)容有號(hào)幾點(diǎn),其中一點(diǎn)是前后端分離的情況下如何不跳轉(zhuǎn)頁(yè)面而是返回需要的返回值。登錄成功,不跳轉(zhuǎn)頁(yè)面,返回自定義返回值在官方文檔第節(jié),有這么一段描述要進(jìn)一步控制目標(biāo),可以使用屬性作為的替代。 在整個(gè)學(xué)習(xí)過(guò)程中,我最關(guān)心的內(nèi)容有號(hào)幾點(diǎn),其中一點(diǎn)是【前后端分離的情況下如何不跳轉(zhuǎn)頁(yè)面而是返回需要的返回值】。下面就說(shuō)一下學(xué)習(xí)結(jié)果,以xml配置位李。 登錄成功,不跳轉(zhuǎn)頁(yè)...
閱讀 989·2019-08-30 15:54
閱讀 1537·2019-08-30 15:54
閱讀 2457·2019-08-29 16:25
閱讀 1361·2019-08-29 15:24
閱讀 822·2019-08-29 12:11
閱讀 2565·2019-08-26 10:43
閱讀 1302·2019-08-26 10:40
閱讀 530·2019-08-23 16:24