I have a requirement to put a strict limit on the number of connections based on a login name and the hostname/port to which the client is connected.
Any thoughts on approach?
I think you can use ChannelGroup for keeping a track of connections. Based on the contents of the channelGroup, make decisions about limiting connectivity. See the code fragment below. All channels which are added into the channelGroup are automatically removed when closed.
class YourHandler extends SimpleChannelHandler {
ChannelGroup channelGroup = new DefaultChannelGroup();
#Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
{
// make a decision if you want to accept connection
// if not just close it using ctc.getChannel().close()
}
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
{
channelGroup.add(ctx.getChannel());
}
}
Related
We have information packs at URLs which need a user right to access, laid out as:
/InfoPacks/InfoPack1/
/InfoPacks/InfoPack2/
etc
A user need ROLE_INFOPACK1 to access the /InfoPacks/InfoPack1/ and ROLE_INFOPACK2 to access the /InfoPacks/InfoPack2/ etc.
We are adding packs all the time so putting adding to WebSecurityConfig() with
.antMatchers("/InfoPacks/InfoPack1/**/*").hasAuthority("ROLE_INFOPACK1)
isnt really a goer as it would imply modifying and re-deploying every time a new pack was created while the configure method in security config got larger and larger.
A custom evaluator would be better. eg something that could call a service with
like:
hasPermission(Authentication auth, String targetURL) {
// search auth.GrantAuthorities for a match to targetURL
}
I see this sort of custom permission expression examples for use with PreAuthorize but doesnt seem to be way to do this with URL authorizeRequests(). (at least in version 4).
Any pointers would be very welcome.
Got it figured out, thanks to http://www.baeldung.com/spring-security-custom-voter.
My web security config now look like:
protected void configure(HttpSecurity http) throws Exception {
http
.headers()
.frameOptions().sameOrigin()
.and()
.csrf().disable()
.exceptionHandling()
.and()
.authorizeRequests()
....
.antMatchers("/Infopacks/**/*").authenticated().accessDecisionManager(accessDecisionManager())
..... etc
}
#SuppressWarnings("unchecked")
#Bean
public AccessDecisionManager accessDecisionManager() {
System.out.println("Arrive AccessDecisionManager");
List<AccessDecisionVoter<? extends Object>> decisionVoters
= Arrays.asList(
new WebExpressionVoter(),
new RoleVoter(),
new AuthenticatedVoter(),
new DynamicVoter());
return new UnanimousBased(decisionVoters);
}
The vote method of DynamicVoter looks (in ugly first test - I know it needs work to plug holes) like:
#Override
public int vote(Authentication a, Object s, Collection clctn) {
String url = ((FilterInvocation) s).getRequestUrl();
int vote = ACCESS_ABSTAIN;
if (url.contains("/Infopack")) {
vote = ACCESS_DENIED;
for (GrantedAuthority ga:a.getAuthorities()) {
if (url.toUpperCase().contains(ga.getAuthority()) ) {
vote = ACCESS_GRANTED;
break;
}
}
}
return vote;
}
The user authorization system thus just needs to add the infopack name as a granted authority to allow user access to the infopack directory. You can add new infopack directories at will without changing the code.
I need to achieve the impact of waitForConfirmsOrDie in core java implementation in spring . In core java it is achievable request wise ( channel.confirmSelect , set Mandatory , publish and Channel.waitForConfirmsOrDie(10000) will wait for 10 sec)
I implemented template.setConfirmCallback ( hope it is same as PublisherCallbackChannel.Listener) and it works great , but ack/nack is at a common place ( confirm call back ) , for the individual sender no idea like waitForConfirmsOrDie , where he is sure within this time ack hasn't came and can take action
do send methods wait for specified period internally like waitForConfirmsOrDie in spring if ack hasn't came and if publisherConfirms is enabled.
There is currently no equivalent of waitForConfirmsOrDie in the Spring API.
Using a connection factory with publisher confirms enabled calls confirmSelect() on its channels; together with a template confirm callback, you can achieve the same functionality by keeping a count of sends yourself and adding a method to your callback to wait - something like...
#Autowired
private RabbitTemplate template;
private void runDemo() throws Exception {
MyCallback confirmCallback = new MyCallback();
this.template.setConfirmCallback(confirmCallback);
this.template.setMandatory(true);
for (int i = 0; i < 10; i++) {
template.convertAndSend(queue().getName(), "foo");
}
confirmCallback.waitForConfirmsOrDie(10, 10_000);
System.out.println("All ack'd");
}
private static class MyCallback implements ConfirmCallback {
private final BlockingQueue<Boolean> queue = new LinkedBlockingQueue<>();
#Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
queue.add(ack);
}
public void waitForConfirmsOrDie(int count, long timeout) throws Exception {
int remaining = count;
while (remaining-- > 0) {
Boolean ack = queue.poll(timeout, TimeUnit.MILLISECONDS);
if (ack == null) {
throw new TimeoutException("timed out waiting for acks");
}
else if (!ack) {
System.err.println("Received a nack");
}
}
}
}
One difference, though is the channel won't be force-closed.
Also, in a multi-threaded environment, you either need a dedicated template/callback per thread, or use CorrelationData to correlate the acks to the sends (e.g. put the thread id into the correlation data and use it in the callback).
I have opened AMQP-717 for us to consider providing something like this out of the box.
I am creating a websocket server that interfaces with a web service endpoint on one side and another which receives web socket connection requests from multiple clients. Here are two approaches that I found:
Implement a web socket configurer and web socket handler as such:
Configurer
#Configuration
#EnableWebSocket
public class TestConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(testHandler(), "/testHandler")
.addInterceptors(new HttpSessionHandshakeInterceptor())
.withSockJS();
}
#Bean
public WebSocketHandler testHandler() {
return new TestHandler();
}
Handler
public class TestHandler extends TextWebSocketHandler {
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
//Take request params and check if a current subscription to external webservice exists, if yes then directly add this session to a map cache repository with the subscription id as key
//If it is a new request then add session to a map cache repository and make new subscription to the external webservice
}
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
}
Configure a message broker endpoint to be subscribed to called /subscribe
public class TestWebSocketConfig implement WebSocketMessageBrokerConfigurer {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> arg0) {}
#Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> arg0) {}
#Override
public void configureClientInboundChannel(ChannelRegistration arg0) {
System.out.println("");
}
#Override
public void configureClientOutboundChannel(ChannelRegistration arg0) {
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
}
#Override
public boolean configureMessageConverters(List<MessageConverter> arg0) {
return true;
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration arg0) {}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/subscribe").withSockJS();
}
Create controller where websocket clients can communicate with
#Controller
public class SubscriptionController {
#Autowired
private SimpMessagingTemplate template;
#MessageMapping("/subscribe1")
#SendTo("/subscribe")
public void addSubscription(String message) {
System.out.println("hi");
}
Here is my question, am I misunderstanding somewhere where these two methods I speak of meant to be combined together? I was using a tomcat implementation of websocket before which matches method 1 which gives me easy direct control over sessions as I would like to be able to reuse web service subscriptions to avoid duplicate request from distinct clients and also a single requests may map to more than one subscription requests to the external webservice. Yet it seems method 2 would push all data requests to the same "/subscribe" endpoint and all connected clients would be receiving the same data, which is not what I am trying to accomplish. It also seems like the message broker api is limited as it does not allow me access to the subscribed sessions where I can control which sessions the receiving data will be sent to. I realized I had to switch to spring websocket as I needed built in browser compatibility fallback offered by SockJS and automatic heartbeat function offered by Stomp.js.
i think i found my answer, method 1 and 2 can be used side by side but not together. Method 2 is used when i want to implement a message broker that can create multiple channel destinations which many users can subscribe to the same destination. Now the question is how i can check whether i can check the number of subscriptions periodically for each existing destination
I have the following Codename One code for accessing a network resource. It is almost an exact copy of the Codename One tutorial for this use case.
public void executeRequest(){
String url = "http://www.random.net";
InfiniteProgress prog = new InfiniteProgress();
final Dialog dlg = prog.showInifiniteBlocking();
ConnectionRequest r = new ConnectionRequest() {
#Override
protected void postResponse() {
//handle changes to my form
}
#Override
protected void readResponse(InputStream input)
throws IOException {
//handle parsing data
}
#Override
protected void handleIOException(IOException err) {
super.handleIOException(err);
}
};
r.setUrl(url);
r.setPost(false);
r.addArgument("arg", "2");
r.setDuplicateSupported(true);
r.setDisposeOnCompletion(dlg);
NetworkManager.getInstance().addToQueue(r);
}
The first time I run it - no problem. If I try to "refresh" my data by calling the same method over again, the app will hang up with the InfiniteProgress dialog spinning forever. Its almost like the first network request is not ever really completing, and then the second one kind of conflicts. Any ideas what I'm doing wrong?
By default duplicate requests to the exact same URL are disabled, try invoking setDuplicatesSuppotred(true) on the connection request.
For future reference, what fixed this for me was to use
NetworkManager.getInstance().addToQueueAndWait(r);
instead. That cleared up most of my problems.
I stucked with the same problem and none of solutions worked. However, I did it this way:
final NetworkManager nm = NetworkManager.getInstance();
nm.setTimeout(3000);
then
protected void postResponse() {
...
nm.shutdown();
}
and call was made as
nm.addToQueueAndWait(request);
Maybe the fact that NetworkManager was made final did the job, but I put "shutdown" just for sure. It worked for me
I'm struggling with the concept of creating a Jedis-client which listens infinitely as a subscriber to a Redis pubsub channel and handles messages when they come in.
My problem is that after a while of inactivity the server stops responding silently. I think this is due to a timeout occurring on the Jedis-client I subscribe with.
Would this likely indeed be the case? If so, is there a way to configure this particular Jedis-client to not timeout? (While other Jedispools aren't affected with some globally set timeout)
Alternatively, is there another (best practice) way of what I'm trying to achieve?
This is my code, (modified/ stripped for display) :
executed during web-server startup:
new Thread(AkkaStarter2.getSingleton()).start();
AkkaStarter2.java
private Jedis sub;
private AkkaListener akkaListener;
public static AkkaStarter2 getSingleton(){
if(singleton==null){
singleton = new AkkaStarter2();
}
return singleton;
}
private AkkaStarter2(){
sub = new Jedis(REDISHOST, REDISPORT);
akkaListener = new AkkaListener();
}
public void run() {
//blocking
sub.psubscribe(akkaListener, AKKAPREFIX + "*");
}
class AkkaListener extends JedisPubSub {
....
public void onPMessage(String pattern, String akkaChannel,String jsonSer) {
...
}
}
Thanks.
ermmm, the below solves it all. Indeed it was a Jedis thing
private AkkaStarter2(){
//0 specifying no timeout.. Overlooked this 100 times
sub = new Jedis(REDISHOST, REDISPORT,0);
akkaListener = new AkkaListener();
}