Spring WebSocket. How to get username via DefaultHandshakeHandler - spring-websocket

I try to implement sending message to a specific user via Spring WebSocket.
Method from controller
simpMessagingTemplate.convertAndSend("/user/" + username + "/queue/messages", messageDto);
But how to get username? As I searched for that need to add DefaultHandshakeHandler to websocket endpoint
I've added:
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/chat" allowed-origins="*">
<websocket:handshake-handler ref="customHandler"/>
<websocket:handshake-interceptors>
<bean class="org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor"/>
</websocket:handshake-interceptors>
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic, /queue" />
public class CustomDefaultHandshakeHandler extends DefaultHandshakeHandler {
#Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
But attributes is null
.....
return principal;
}
}
For example here: Sending message to specific user using spring
String name = (String)attributes.get("name");
But attributes are empty(null).
What should I do to get username attribute?
frontend use Angular4 and stomp over WebSocket service
this.stomp.configure({
host: AppSettings.WEBSOCKET_HOST + '/chat',
headers: {'username': 'test'},
queue:{'init':false}
});

Related

Authorization with .net core 3.1 with JWT Token response from API

Im new to C# and im struggling with authorization in ASP.Net Core 3.1 MVC web application.I know that there is a lot of instruction on google, i've been reading and watching for 3 days but can not work this out because every instruction i found, it use another way and im really confused.
The idea of my system is:
Step 1. I POST username and password to my API and it'll response with JWT Token (if account is correct)
Step 2. I decode the token and get the username, email, role for my website, set HttpClient header for another requests.
My problems:
How and where to set HttpClient header (with my token) only one time when user login
How to force users stay at the Login page if they aren't login yet
Here's my Login method
[HttpPost, AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel account)
{
string url = "accounts/signin";
var response = await new HttpClientHelper<LoginViewModel>().PostRequest(url, account);
var userToken = JsonConvert.DeserializeObject<UserToken>(response);
Console.Out.WriteLine(userToken.Token);
if (userToken.Token != null)
{
var token = new JwtSecurityToken(jwtEncodedString: userToken.Token);
var userId = token.Claims.First(c => c.Type == "userId").Value;
var username = token.Claims.First(c => c.Type == "unique_name").Value;
var role = token.Claims.First(c => c.Type == "role").Value;
HttpContext.Session.SetString("token", token.ToString());
HttpContext.Session.SetString("userId", userId);
HttpContext.Session.SetString("username", username);
HttpContext.Session.SetString("role", role);
return RedirectToAction("Home", "Index");
}
return RedirectToAction("Login", "Login");
}
My model to receive response from api
public class UserToken
{
public string Token { get; set; }
public string ValidFrom { get; set; }
public string ValidTo { get; set; }
}
FYI: Ive already recived the response from api and got the Token, but ive to set HttpClient header every time i make a request..
How and where to set HttpClient header (with my token) only one time when user login
As far as I know, we couldn't set the httpclient header only one time when user login. Normally, we could store the token into session or cookie and then read it from cookie or session when you want to send request to web api.
How to force users stay at the Login page if they aren't login yet
For this requirement, I suggest you could consider using the authentication middleware to achieve your requirement.
You could check the user's session inside this middleware, if this user doesn't contains the session then you could modify the request path to login page.
More details, you could refer to below example:
//Below cods should add after app.usesession in startup.cs Configure method
app.Use((context, next) =>
{
string token = context.Session.GetString("token");
if (token == null)
{
context.Request.Path = "/account/login";
}
return next.Invoke();
});

Swagger: Authorization token is not passed into requests headers

i use springfox 2.9.2
I have api like:
#Api(tags = "Users")
#RestController
#RequestMapping("users")
public class UsersController {
#ApiOperation(value = "Creates a user")
#ApiResponses(value = {
#ApiResponse(code = 201, message = "user created"),
#ApiResponse(code = 401, message = "not authorized")})
#PostMapping(value = "/add")
public ResponseEntity addUser(#Valid #RequestBody UserDTO userDTO) {
...
}
to make this call user needs authorization token
Authorization: Bearer {token}
witch comes from authentication server.
i try to make first call to this server in swagger and pass it to controller requests like the one above.
So i do
#Bean
public Docket api() {
final String swaggerToken = "";
return new Docket(DocumentationType.SWAGGER_2)
#Bean
public .select()
.apis(RequestHandlerSelectors.basePackage("com.mbv.coros.notification.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(apiEndPointsInfo())
.securitySchemes(Arrays.asList(securityScheme()))
.securityContexts(Arrays.asList(securityContext()))
.useDefaultResponseMessages(false);
}
private SecurityScheme securityScheme() {
GrantType grantType = new ResourceOwnerPasswordCredentialsGrant(AUTH_SERVER + "/token");
SecurityScheme oauth = new OAuthBuilder().name("spring_oauth")
.grantTypes(Arrays.asList(grantType))
.scopes(Arrays.asList(scopes()))
.build();
return oauth;
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return Lists.newArrayList(
new SecurityReference("JWT", authorizationScopes));
}
on Swagger ui authorization call returns the token successfully but it doesnt add it to request headers. It generates
curl -X GET "http://localhost:8080/users/get" -H "accept: */*"
if i set token like:
.securitySchemes(Arrays.asList(apiKey()))
private ApiKey apiKey() {
return new ApiKey("JWT", AUTHORIZATION_HEADER, "header");
}
it works perfectly.
any ideas why is this happening?
As far as I know, Swagger uses token only if you configure it to do so, and the configuration is done using the "Authorize" button on the top right of the Swagger UI page.
So, the ideal case would be:
Trigger Auth call which returns the token
Copy the token; Click on "Authorize" button and paste the JWT token in "Bearer "
After this, all the subsequent calls are supposed to use this token until you press logout.

How to use OAuth with Swagger and NSwagStudio

I'm trying to generate a C# client for an API that has provided a swagger.json file to me, located at this link;
https://api.ekm.net/swagger/v1/swagger.json
Using the NSwagStudo application I am able to import the configuration file and generate a file called Client.cs which implements a class called Client and it has methods on it that match the API.
However when I call any of the methods I get an "Unauthorized" exception and I can not find any way to provide the OAuth key and secret to the client or anyone doing similar with other authentication methods.
Inspecting the swagger configuration files does show that OAuth is indicated as the authentication method as follows;
"securityDefinitions": {
"OAuth": {
"flow": "accessCode",
"authorizationUrl": "https://api.ekm.net/connect/authorize",
"tokenUrl": "https://api.ekm.net/connect/token",
"scopes": {
"tempest.customers.read": "Read a shop's customers.",
"tempest.customers.write": "Modify a shop's customers.",
"tempest.orders.read": "Read a shops orders.",
"tempest.orders.write": "Modify a shop's orders.",
"tempest.products.read": "Read a shop's products.",
"tempest.products.write": "Modify a shop's products.",
"tempest.categories.read": "Read a shop's categories.",
"tempest.categories.write": "Modify a shop's categories.",
"tempest.settings.orderstatuses.read": "Read a shop's order statuses.",
"tempest.settings.domains.read": "Read a shop's domains."
},
"type": "oauth2",
"description": "In order to ensure the safety of our users data, we require all partner applications to register via the [Partner Dashboard](https://partners.ekm.net/). Once registered, partners are provided with an application key, which can be used during an OAuth2 handshake to create a token. This token can then used to make requests on behalf of a merchant."
}
},
My test code is as follows;
static void Main(string[] args)
{
var swagClient = new Client();
var ords = swagClient.ApiV1OrdersGetAsync(1, 100).Result; // This call throws SwaggerException: Unauthorized
}
The Client class does not have any obvious methods or properties to set the security values or any constructor parameters.
Does anyone have an example of how to achieve this?
I agree. It's kind of strange that it doesn't just accept some kind of "insert JWT here".
Anyway, this is how I've fixed it:
Inject HttpClient
Tick on the box named "Inject HttpClient via constructor" in NSwagStudio
CustomMessageHandler
Introduce a custom HttpMessageHandler:
internal class AuthTokenHttpMessageHandler: HttpClientHandler
{
private readonly Action<HttpRequestMessage, CancellationToken> _processRequest;
public AuthTokenHttpMessageHandler(Action<HttpRequestMessage, CancellationToken> processRequest)
{
_processRequest = processRequest;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
_processRequest(request, cancellationToken);
return base.SendAsync(request, cancellationToken);
}
}
This handler accepts a delegate in which you can provide your JWT.
Integrating with your client
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
public class MyService : IDisposable
{
private readonly AuthTokenHttpMessageHandler _messageHandler;
private readonly HttpClient _httpClient;
private readonly MyNSwagClient _client;
public MyService()
{
_messageHandler = new AuthTokenHttpMessageHandler((req, _) =>
{
req.Headers.Authorization = new AuthenticationHeaderValue("bearer", "your token goes here");
});
_httpClient = new HttpClient(_messageHandler);
_client = new MyNSwagClient(_httpClient);
}
public async Task<SomeModel> GetStuffAsync(string paramenter1)
{
return await _client.StuffGetAsync(parameter1);
}
public void Dispose()
{
_httpClient?.Dispose();
_messageHandler?.Dispose();
}
}
I hope this helps you

Web API uri to routed and defined action returns 404

I don't understand how System.Web.Http method attributes are used. I have this method Logon in my Auth web API controller (ASP.Net MVC 4):
public HttpResponseMessage Logon(string email, string password)
{
User user = UserSrv.Load(email);
if (user != null && user.CheckPassword(password))
return this.Request.CreateResponse<User>(HttpStatusCode.OK, user);
else
return this.Request.CreateResponse(HttpStatusCode.BadRequest, "Invalid username or password");
}
The WebApiConfig.cs file is default:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Formatters.Remove(config.Formatters.XmlFormatter);
}
}
As is, a GET method returns 405 method not allowed. As do a PUT, HEAD and all other verbs I've tried. But for a POST, it returns a 404 not found with the following JSON:
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:8080/api/Auth/Logon'.",
"MessageDetail": "No action was found on the controller 'Auth' that matches the request."
}
If a add [HttpPost] attribute before method definition, I get the exact same results. With HttpGet, of course GET requests are OK. Combination of both attributes doesn't change anything. How come POST requests are not correctly routed?
Edit:
The POST request does not match because the Uri is http://localhost:8080/api/Auth/Logon, with no query parameter. If I set default values for the email and password parameters, the method matches. But I thought that MVC was smart enough to match the request content to the action parameters. Do I really need to read the content stream to find the argument values?
It's apparently impossible with web Api to bind a post request with multiple parameters to an action. The easiest solution is to send a json object and parse it. My method now looks like
[HttpPost]
public HttpResponseMessage Logon(JObject dto)
{
dynamic json = dto;
string email = json.email;
string password = json.password;
User user = UserSrv.Load(email);
if (user != null && user.CheckPassword(password))
return this.Request.CreateResponse<User>(HttpStatusCode.OK, user);
else
return this.Request.CreateResponse(HttpStatusCode.BadRequest, "Invalid username or password");
}
See http://www.west-wind.com/weblog/posts/2012/May/08/Passing-multiple-POST-parameters-to-Web-API-Controller-Methods
and http://www.west-wind.com/weblog/posts/2012/Sep/11/Passing-multiple-simple-POST-Values-to-ASPNET-Web-API

Grails security plugin: Redirecting post and passing its data

On a gsp, I have a submit that goes to a controller action. In that action, I want to do some processing, then pass the post parameters from the gsp to another POST method. How can I do this?
Basically, are POST parameters treated specially in the call to redirect? Because I'm trying to do the following. I have some custom gsp that I want to use to create a user account.
<form action='save' method='POST' id='createForm' class='cssform' autocomplete='off'>
<input type='text' class='text' name='j_username'/>
<input type='password' class='text' name='j_password'/>
<input type='submit' value='Create' />
</form>
I have a save action in my controller that I want to create the user on the db, then login.
def save = {
// creating user on DB
def config = SpringSecurityUtils.securityConfig
String postUrl = "${request.contextPath}${config.apf.filterProcesse sUrl}"
redirect(uri: postUrl, params: params)
}
The redirect to j_security_check causes a login failure. I'm suspecting that's due to the redirect.
You can use redirect:
redirect(action:'xyz', controller:'Abc', params:params)
or you can chain actions:
chain(action:'abc', controller:'Xyz',model:["myObject":new MyDomain(params)])
Grails - Controllers - Redirects
In class RequestHolderAuthenticationFilter
add this "super.setPostOnly(false);"
public class RequestHolderAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
SecurityRequestHolder.set((HttpServletRequest)request, (HttpServletResponse)response);
try {
super.setPostOnly(false);
super.doFilter(request, response, chain);

Resources