I want to develop notification sender in real time with Spring Boot but I can't use Stomp and sockjs, so I need implement raw WebSocket but I can't find out how to set Principal in WebSocket connection beacuse I want to Authenticate with JWT token. So, where or how can I set principal.
I'am using these;
WebSocketConfig.java :
#EnableWebSocket
#Configuration
public class WebSocketConfig implements WebSocketConfigurer {
#Autowired
WebSocketNotificationSenderService senderService;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry
webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(createHandler(),
"/handler").addInterceptors(new HttpSessionHandshakeInterceptor()
{
#Override
public void afterHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
#Nullable Exception ex) {
super.afterHandshake(request, response, wsHandler, ex);
}
#Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
return super.beforeHandshake(request, response,
wsHandler, attributes)
}
});
}
#Bean
public WebSocketHandler createHandler() {
return new MyHandler(senderService);
}
}
MyHandler.java :
#Component
public class MyHandler extends TextWebSocketHandler {
WebSocketNotificationSenderService senderService;
public MyHandler(WebSocketNotificationSenderService senderService){
this.senderService = senderService;
}
#Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
senderService.addToSession(session);
}
}
WebSocketNotificationSenderService.java :
#Service
public class WebSocketNotificationSenderService implements
IWebSocketSenderService<WrapperWsNotification> {
private List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();
private ObjectMapper mapper = new ObjectMapper();
public void addToSession(WebSocketSession session) {
sessions.add(session);
}
#Override
public void convertAndSend(WrapperWsNotification payload) throws JsonProcessingException {
String payloadString = mapper.writeValueAsString(payload);
sessions.stream().forEach(session -> {
try {
session.sendMessage(new TextMessage(payloadString));
} catch (IOException e) {
e.printStackTrace();
}
});
}
#Override
public void convertAndSendToUser(String user, WrapperWsNotification payload) throws
JsonProcessingException {
String payloadString = mapper.writeValueAsString(payload);
sessions.forEach(session -> {
if (session.getPrincipal().getName().equals(user)) {
try {
session.sendMessage(new TextMessage(payloadString));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
My Notification Sender to websocket;
#Component
public class NotificationConsumer {
#Autowired
WebSocketNotificationSenderService webSocket;
private Logger logger = LoggerFactory.getLogger(NotificationConsumer.class);
public void onReceiveNotification(String object) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
WrapperWsNotification wrapperWsNotification= objectMapper.readValue(object, WrapperWsNotification.class);
logger.info("User where coming from redis " + wrapperWsNotification.getUser().getUsername());
webSocket.convertAndSendToUser(wrapperWsNotification.getUser().getUsername(), wrapperWsNotification);
}
}
I find out solution and added an example
Related
I have a question: Why is it that when getAttributes() returns null, every subsequent request doesn't go through the MyFilterSecurityInterceptor class and it doesn't intercept for the next request or every request after that?
MyFilterSecurityInterceptor
#Service
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
#Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;
#Autowired
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
super.setAccessDecisionManager(myAccessDecisionManager);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
//There is a blocked url in fi
//Call the getAttributes(Object object) method of MyInvocationSecurityMetadataSource to get all the permissions corresponding to fi
//Call the decide method of MyAccessDecisionManager to verify whether the user's permissions are sufficient
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//Execute the next interceptor
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
#Override
public void destroy() {
}
#Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
#Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
}
MyFilterSecurityMetadataSource
public class MyFilterSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
#Override
public List<ConfigAttribute> getAttributes(Object object) {
FilterInvocation fi = (FilterInvocation) object;
HttpServletRequest request = fi.getRequest();
HttpMethod httpMethod = HttpMethod.valueOf(fi.getRequest().getMethod());
// Bypassing Security check for /js, /css and /images url
if (new AntPathRequestMatcher("/js/**").matches(request)
|| new AntPathRequestMatcher("/css/**").matches(request)
|| new AntPathRequestMatcher("/images/**").matches(request)
|| new AntPathRequestMatcher("/login").matches(request)
|| new AntPathRequestMatcher("/").matches(request)
|| new AntPathRequestMatcher("/h2/**").matches(request)) {
return SecurityConfig.createList(new String[] { "Allow" });
}
try {
Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication()
.getAuthorities();
for (GrantedAuthority grantedAuthority : authorities) {
if(new AntPathRequestMatcher(grantedAuthority.toString()).matches(request)) {
return SecurityConfig.createList(new String[] { "Allow" });
}
}
} catch (Exception e) {
return SecurityConfig.createList(new String[] { "Deny" });
}
return null;
}
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
MyAccessDecisionManager
public class MyAccessDecisionManager implements AccessDecisionManager {
#Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
throws AccessDeniedException, InsufficientAuthenticationException {
if (configAttributes == null || configAttributes.size() == 0) {
return;
}
Iterator<ConfigAttribute> ite = configAttributes.iterator();
if(ite.next().toString().equalsIgnoreCase("Allow")) {
return;
}
else {
System.out.println("Access is denied");
throw new AccessDeniedException("Access is denied");
}
}
#Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
#Override
public boolean supports(Class<?> clazz) {
return true;
}
}
config security
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/h2/**").permitAll()
.anyRequest().authenticated()
.and().httpBasic()
.and().formLogin()
.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
}
}
and I have set a breakpoint in debug to check, it really only stops on the first request, and after return null is returned incoming requests are not stopped
So, I think it is because your checked exception in your filter. Returning null is not enough significant.
If I were you, I will use a RuntimeException which will stop the workflow at that moment, add these exception inside the webapplication exception handler and make a custom webpage to display a message for these exception or for all requests with 403 http status code (not authorized).
Did you add your custom interceptor inside your Spring application config ? Inside the main file : SpringWebapplication.java as a Bean :
#Bean(name="securityInterceptor")
public MyFilterSecurityInterceptor securityInterceptor() {
return new MyFilterSecurityInterceptor();
}
Or inside WebConfig.java :
private MyFilterSecurityInterceptor securityInterceptor;
//...
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(securityInterceptor);
registry.addInterceptor(localeChangeInterceptor);
}
I am new to JWT. I create my own microservice and want to introduce JWT authentication. I have one website that issues a token and in the other I want to check the correctness of this token. I want to do this without connecting the second site to db. This approach seems to me appropriate and best for user data.
I have following payload of token:
{
"sub": "Marek",
"auth": [
{
"authority": "ROLE_USER"
}
],
"iat": 1574091010,
"exp": 1574091210
}
Its my code:
WebSecurityConfig
#Autowired
private JwtTockenCreator jwtTockenCreator;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers("/user/login").permitAll()
.antMatchers("/user/addUser").permitAll()
.anyRequest()
.authenticated();
http.exceptionHandling().accessDeniedPage("/login");
http.apply(new JWTConfigurer(jwtTockenCreator));
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
}
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
JwtTokenFilter
public class JwtTokenFilter extends OncePerRequestFilter {
private JwtTockenCreator jwtTockenCreator;
public JwtTokenFilter(JwtTockenCreator jwtTockenCreator) {
this.jwtTockenCreator = jwtTockenCreator;
}
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
FilterChain filterChain) throws ServletException, IOException {
String token = jwtTockenCreator.resolveToken(httpServletRequest);
try {
if (token != null && jwtTockenCreator.validateToken(token)) {
Authentication auth = jwtTockenCreator.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
}
} catch (CustomException ex) {
// this is very important, since it guarantees the user is not authenticated at
// all
SecurityContextHolder.clearContext();
httpServletResponse.sendError(ex.getHttpStatus().value(), ex.getMessage());
return;
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
JwtTockenCreator
public class JwtTockenCreator {
#Value("${security.secretKey}")
private String secretKey;
#PostConstruct
protected void init() {
secretKey = Base64.getEncoder().encodeToString(secretKey.getBytes());
}
public Authentication getAuthentication(String token) {
Claims claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
Collection<? extends GrantedAuthority> authorities = Arrays.asList(claims.get(secretKey).toString().split(",")).stream()
.map(authority -> new SimpleGrantedAuthority(authority)).collect(Collectors.toList());
User principal = new User(claims.getSubject(), "", authorities);
return new UsernamePasswordAuthenticationToken( principal,"",authorities);
}
public String getUsernameFromToken(String token) {
return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token).getBody().getSubject();
}
public String resolveToken(HttpServletRequest req) {
String bearerToken = req.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
throw new CustomException("Expired or invalid JWT token", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
JWTConfigurer
public class JWTConfigurer extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
private JwtTockenCreator jwtTockenCreator;
public JWTConfigurer(JwtTockenCreator jwtTockenCreator) {
this.jwtTockenCreator = jwtTockenCreator;
}
#Override
public void configure(HttpSecurity http) throws Exception {
JwtTokenFilter customFilter = new JwtTokenFilter(jwtTockenCreator);
http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class);
}
}
UserController
#CrossOrigin
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
RestTemplate restTemplate;
#Value("${hostname}")
public String hostname;
#Value("${user.port}")
public String userPort;
#PostMapping("/login")
public ResponseEntity<String> login(#RequestBody User user) {
String urlUser = hostname + userPort + "/user/login";
String token = restTemplate.postForObject(urlUser, user, String.class);
return ResponseEntity.ok(token);
}
#PreAuthorize("hasRole('USER')")
#PostMapping("/addUser")
public ResponseEntity<String> registerAction(#RequestBody User user) {
String urlUser = hostname + userPort + "/user/addUser";
String token = restTemplate.postForObject(urlUser, user, String.class);
return ResponseEntity.ok(token);
}
}
In Eclipse doesn't give any errors. That's why I don't know what I'm doing wrong
when I want to call / user / addUser and add a new user nothing happens. In the User service I call, I have a function responsible for adding users and it works correctly when I refer to it directly. And if I want to do it through my Rest Api it doesn't work anymore. And it is my problem that I do not know what can happen because I have no mistake. I remind you that I am still learning and I am asking for understanding
I want to use custom exception handlers for my application. But, it is not working properly.
Here is my code
AuthenticationFilter.java
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
if (!bAuthorize) {
chain.doFilter(request, response);
return;
}
HttpServletRequest req = (HttpServletRequest) request;
String namespace = getPathParamFromRequest(req, NAMESPACE_PATH_PREFIX);
String userId = getPathParamFromRequest(req, USER_PATH_PREFIX);
AuthContext auth = null;
RequestPathContext rpc = pathsList.getMatchingContext(req.getRequestURI(), HttpMethod.valueOf(req.getMethod()));
if (rpc != null)
auth = rpc.getAuthContext();
if (auth != null) {
// Authentication process
} else {
throw new UnauthorizedException();
}
}
ApplicationExceptionHandler.java
public class ApplicationExceptionHandler {
#ExceptionHandler(UnauthorizedException.class)
public ResponseEntity<ErrorEntity> applicationxception(final UnauthorizedException e) {
ErrorEntity errorEntity = new ErrorEntity(e.getNumericErrorCode(), e.getErrorCode(), e.getErrorMessage());
return new ResponseEntity<>(errorEntity, HttpStatus.valueOf(e.getHttpStatus()));
}
}
AuthFilterRegistration.java
#Configuration
public class AuthFilterRegistration {
#Autowired
private ApplicationContext context;
#Bean
public FilterRegistrationBean<AuthenticationFilter> loggingFilter() {
FilterRegistrationBean<AuthenticationFilter> registrationBean
= new FilterRegistrationBean<>();
registrationBean.setFilter(context.getBean(AuthenticationFilter.class));
registrationBean.addUrlPatterns( "/public/*");
return registrationBean;
}
#Bean
public AuthenticationFilter getAuthFilter() {
return new AuthenticationFilter();
}
#Bean
public ApplicationExceptionHandler getErrorHandler() {
return new ApplicationExceptionHandler();
}
}
ErrorEntity.java
public class ErrorEntity extends BaseErrorEntity {
String errorMessage;
Map<String, String> messageVariables;
public ErrorEntity() {
}
public ErrorEntity(int numericErrorCode, String errorCode, String errorMessage) {
this(numericErrorCode, errorCode, errorMessage, null);
}
public ErrorEntity(int numericErrorCode, String errorCode, String errorMessage, Map<String, String> messageVariables) {
this.numericErrorCode = numericErrorCode;
this.errorCode = errorCode;
this.errorMessage = errorMessage;
this.messageVariables = messageVariables;
}
}
Using those code, I want to have an exception error like this
{
"numericErrorCode": 2001,
"errorCode": "errors.net.myproject.platform.unauthorized",
"errorMessage": "unauthorized"
}
which is the instance of ErrorEntity, but I got this output
{
"timestamp": "2019-02-01T04:41:14.337+0000",
"status": 500,
"error": "Internal Server Error",
"message": "unauthorized",
}
From the example it is clear that I cannot override the default Java exception completely. Only the message part that is altered successfully. Do I miss something here?
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#ControllerAdvice
public class ApplicationExceptionHandler extends ResponseEntityExceptionHandler {
#ResponseBody
#ExceptionHandler(UnauthorizedException.class)
public ResponseEntity<ErrorEntity> applicationxception(final UnauthorizedException e) {
ErrorEntity errorEntity = new ErrorEntity(e.getNumericErrorCode(), e.getErrorCode(), e.getErrorMessage());
return new ResponseEntity<>(errorEntity, HttpStatus.valueOf(e.getHttpStatus()));
}
#ResponseBody
#ExceptionHandler(RetrievedProfileException.class)
public ResponseEntity<ErrorEntity> applicationexception(final RetrievedProfileException e) {
ErrorEntity errorEntity = new ErrorEntity(e.getNumericErrorCode(), e.getErrorCode(), e.getErrorMessage());
return new ResponseEntity<>(errorEntity, HttpStatus.valueOf(e.getHttpStatus()));
}
I just extend this class org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler because its pre- requsite
Secondly i used #ControllerAdvice
and lastly i used #ResponseBody
Use Exception Handler this way plus you should override the exception in this way.
This is a part of my class, I want to test:
public class PrefPanel extends Composite {
private static PrefPanelUiBinder uiBinder = GWT.create(PrefPanelUiBinder.class);
interface PrefPanelUiBinder extends UiBinder<Widget, PrefPanel> {}
public PrefPanel(GlobalParams globalParams) {
initWidget(uiBinder.createAndBindUi(this));
String url = URL.encode(globalParams.getBaseUrl() + "book.html");
RequestBuilder builder = new RequestBuilder(RequestBuilder.POST, url);
try {
Request response = builder.sendRequest(jsonString, new RequestCallback() {
#Override
public void onError(Request request, Throwable exception) {
displayError("Error");
}
#Override
public void onResponseReceived(Request request, Response response) {
updateBookList(response.getText());
}
});
} catch (RequestException e) {
displayError("Error");
}
Here is a part of my test class:
#RunWith(GwtMockitoTestRunner.class)
public class PositionServiceTest {
#Mock RequestBuilder builder;
#Mock GlobalParams globalParams;
#Mock URL url;
private PrefPanel prefPanel;
#Before
public void setup() {
GwtMockito.useProviderForType(RequestBuilder.class, new FakeProvider() {
#Override
public Object getFake(Class aclass) {
return builder;
}
});
when(globalParams.getBaseUrl()).thenReturn("http://localhost/");
prefPanel = new PrefPanel(globalParams);
...
When I start to debug I get an error message:
- url cannot be empty
- java.lang.IllegalArgumentException
- at com.google.gwt.http.client.StringValidator.throwlfEmptyOrNull(StringValidator.java:52)
- ...
The error occurs on the line where I create the RequestBuilder (new RequestBuilder). I have no idea how to create a new instance of RequestBuilder. Could you give me a clue?
I have heard that gwtmockit can't handle constructors. Is there a way to avoid the new RequestBuilder? Do I have to use powermockito?
i want to add my Custom MappingJackson2HttpMessageConverter to Spring Boot . it set successful as Converter but did not use it for converting ...
i see this error just for spring 4.3 and upper. it successful set in spring 4.0.3
How do i correct this converter ???
here is my code
public class ResponseViewEntity<T> extends
ResponseEntity<ContainerViewEntity<T>> {
private Class<? extends View.Base> view;
public ResponseViewEntity(HttpStatus statusCode) {
super(statusCode);
}
public ResponseViewEntity(T body, HttpStatus statusCode) {
super(new ContainerViewEntity<T>(body, View.Base.class), statusCode);
}
public ResponseViewEntity(T body, Class<? extends View.Base> view, HttpStatus statusCode) {
super(new ContainerViewEntity<T>(body, view), statusCode);
}
Converter :
public class JsonViewMessageConverter extends
MappingJackson2HttpMessageConverter {
private ObjectMapper objectMapper = new HibernateAwareObjectMapper();
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
JavaType javaType = getJavaType(clazz);
try {
return objectMapper.readValue(inputMessage.getBody(), javaType);
} catch (JsonProcessingException ex) {
throw new HttpMessageNotReadableException("Could not read JSON: "
+ ex.getMessage(), (Throwable) ex);
}
}
protected void writeInternal(Object object, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
if (object instanceof ContainerViewEntity
&& ((ContainerViewEntity) object).hasView()) {
writeView((ContainerViewEntity) object, outputMessage);
} else {
super.writeInternal(object, outputMessage);
}
}
protected void writeView(ContainerViewEntity view,
HttpOutputMessage outputMessage) throws IOException,
HttpMessageNotWritableException {
JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders()
.getContentType());
ObjectWriter writer = getWriterForView(view.getView());
JsonGenerator jsonGenerator = writer.getFactory().createGenerator(
outputMessage.getBody(), encoding);
try {
writer.writeValue(jsonGenerator, view.getObject());
} catch (IOException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: "
+ ex.getMessage(), (Throwable) ex);
}
}
private ObjectWriter getWriterForView(Class<?> view) {
objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, false);
return objectMapper.writer().withView(view);
}
protected JavaType getJavaType(Class<?> clazz) {
return TypeFactory.defaultInstance().constructType(clazz);
}
public ObjectMapper getObjectMapper() {
return objectMapper;
}
protected JsonEncoding getJsonEncoding(MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
Charset charset = contentType.getCharset();
for (JsonEncoding encoding : JsonEncoding.values()) {
if (!charset.name().equals(encoding.getJavaName()))
continue;
return encoding;
}
}
return JsonEncoding.UTF8;
}
public void setObjectMapper(ObjectMapper objectMapper) {
Assert.notNull((Object) objectMapper,
(String) "ObjectMapper must not be null");
this.objectMapper = objectMapper;
}
and My config
#Bean
public JsonViewMessageConverter mappingJackson2HttpMessageConverter() {
JsonViewMessageConverter jsonConverter = new JsonViewMessageConverter();
ObjectMapper objectMapper = new HibernateAwareObjectMapper();
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
#Override
public void configureMessageConverters(
List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
}