I am trying to implement a browser based single sign on using SPNEGO with Tomcat.
I have followed all the instructions on these two pages:
http://spnego.sourceforge.net/pre_flight.html
http://spnego.sourceforge.net/spnego_tomcat.html
When I accessed hello_spnego.jsp work fine and return user
but I need to add configuration in spring boot standalone application here is class configuration
#WebFilter("/*")
public class ExampleSpnegoAuthenricatorValue implements Filter {
private final Logger LOGGER = Logger.getLogger(ExampleSpnegoAuthenricatorValue.class.getName());
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
LOGGER.info("start Here -------------------------------------");
LOGGER.info(httpServletRequest.getRemoteUser());
LOGGER.info(httpServletRequest.getSession().getId());
Enumeration<String> headerNames = httpServletRequest.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
System.out.println("Header: " + httpServletRequest.getHeader(headerNames.nextElement()));
}
}
LOGGER.info(httpServletRequest.getRequestedSessionId());
LOGGER.info("end Here -------------------------------------");
// Get the IP address of client machine.
// Log the IP address and current timestamp.
// Pass request back down the filter chain
filterChain.doFilter(servletRequest,servletResponse);
}
#Override
public void destroy() {
}
}
RemoteUser is null ?? and this error showing :
HTTP Status 500 - GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
type Exception report
message GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
I have verified that the account used to talk to the KDC is working correctly. Also, I have the username and password specified in the web.xml file, so I don't have a separate KeyTab file.
For diagnosis purposes I'm add two files krb5.conf and login.conf files:
Since I don't have a keytab file, it's not mentioned in the login.conf file.
FYI, I'm using Tomcat 8 and JDK 1.8.
I would really appreciate some insight on what's happening here. If you need more information, please let me know. Thanks in advance!
Related
My application sits behind a caching server that acts as a proxy. I'd like to limit access to the application so that it can only be accessed via the proxy. I first tried using a RemoteAddrFilter, however this filters based on the X-Forwarded-For header which is the IP address of the user connecting to the proxy, not the IP address of the proxy (This header is forwarded instead of being overwritten by the proxy). I decided to write my own filter based off of the RemoteAddrFilter:
public class IpFilter extends RequestFilter {
#Override
protected Log getLogger() {
return null;
}
private void sendErrorWhenNotHttp(ServletResponse response) throws IOException {
response.setContentType("text/plain");
response.getWriter().write(sm.getString("http.403"));
response.getWriter().flush();
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (/*check IP*/) {
filterChain.doFilter(servletRequest, servletResponse);
} else if (servletResponse instanceof HttpServletResponse) {
((HttpServletResponse) servletResponse).sendError(denyStatus);
} else {
sendErrorWhenNotHttp(servletResponse);
}
}
}
This gives me much more control over what can be accessed, however I'm unsure what to put into this if statement.
From what I can tell, given the parameters exposed here, the only accessible IP addresses are via headers, which do not represent the IP address of the proxy.
How can I get the IP address of the proxy?
I'm working on springboot project and we are using openId keycloak for authentication. I'm delaing with Multitenancy concept too. I want to sent custom header as request or either response and the same should be captured in APM as metadata. My current approach is as follows:
public class PreAuthFilter extends KeycloakPreAuthActionsFilter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
((HttpServletResponse) response).addHeader("X-Realm",realm);
super.doFilter(request, response, chain);
}
But with above code i'm getting multiple response metatdata in APM
http.response.headers.X-Realm.0
http.response.headers.X-Realm.1
http.response.headers.X-Realm.2
http.response.headers.X-Realm.3
My expectation was single realm in APM Metadata
http.response.headers.X-Realm = "value"
I think SimpleHttpFacade is getting intialized during resolving deployment multiple times hence adding the response.
Need Suggestion
Thanx.
It appears this could be that the issue is more likely related to your application context spring and filters.
Since it's spring could you try OncePerRequestFilter ?
import org.springframework.web.filter.OncePerRequestFilter;
#Named
public class ApmFilter extends OncePerRequestFilter {
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// do apm things
filterChain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
i have a few java micro services deployed on open shift . all of them are protected by a api-gateway application which uses keycloak for authentication & Authorization.
Down stream services need to log which user perform certain actions.
in my api-gateway application properties i have already set zuul.sensitiveHeaders to empty
zuul.sensitiveHeaders:
i can see bearer token in the downstream applications .
but how do i get the principal/user from token as downstream applications don't have keycloak dependency in gradle. ( if i add the dependency , i need to reconfigure realm and other properties ) .. is this the right way to do ?
i also tried adding a filter in api-gateway to separately set the user_name in header
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
System.out.println(" Filter doFilter "+req.getUserPrincipal());
if(req.getUserPrincipal() != null ){
res.setHeader("MYUSER",req.getUserPrincipal()==null?"NULL":req.getUserPrincipal().getName());
}
chain.doFilter(request, response);
}
But when i try to get the header in downstream microservices is null.
I wouldn't recommend doing this, or assuming that your non-web facing apps are completely secure. Realistically you should be re-validating the bearer token.
What you need is a zuul filter to add a header to the request. This is mostly from memory and you could update the filter to check if it should filter or not, that the request doesn't already contain an expected header etc.
#Component
public class AddUserHeader extends ZuulFilter {
private static final Logger LOG = LoggerFactory.getLogger(AddUserHeader.class);
#Override
public String filterType() {
return "pre";
}
#Override
public int filterOrder() {
return 0;
}
#Override
public boolean shouldFilter{
return true;
}
#Override
public Object run() {
RequestContext.getCurrentContext().addZuulRequestHeader("MYUSER", SecurityContextHolder.getAuthentication().getPrincipal().getName());
return null;
}
I am planning to secure my REST server with external Identity provider(WSo2) or local spring ouath token generator. Token generation mode(Eternal/Internal) will be made configurable. I took the example listed below and modified the CORSFilter class to verify token with External provider(WSO2). I am not sure how to skip the local token generation and access my resource server here. I guess the authorization and Resource server needs to be separate module to achieve what I need.I am completely new to spring ouath so not sure if this is the right way. I tried all over the internet to find better example but not able to get one. Any pointers/examples will be helpful.
URL(example): http://websystique.com/spring-security/secure-spring-rest-api-using-oauth2/
public class CORSFilter extends OncePerRequestFilter {
private final Logger LOG = LoggerFactory.getLogger(CORSFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws ServletException, IOException {
//Read the keygen mode if it is External validation do this else skip all this so that local token is generated.
LOG.info("###token is"+ req.getHeader("Authorization"));
String token = req.getHeader("Authorization");
TokenValidator obj = new TokenValidator(token);//This class validates the Access token with WSO2
boolean tokenStatus = obj.gettokenStatus();
if(tokenStatus){
res.setHeader("Access-Control-Allow-Headers", "*");
res.setHeader("Access-Control-Max-Age", "3600");
chain.doFilter(req, res);
}else{
LOG.info("token validation is not scuesfull");
throw new SecurityException();
}
}
I'm doing experiments with Spring 4 websockets and stomp, and I have a hard time figuring out how to get/set the current user and other session attributes in a message handling method annotated with #MessageMapping.
The documentation says that the message handling methods can take a Principal as argument, and I found that the principal is retrieved by Spring by calling getUserPrincipal() on the native socket session, and then associated with the socket session, but I haven't found any way to easily customize this behavior, other than writing a servlet filter and wrap the original request into a wrapper returning the principal found in my cookie.
So my questions are:
How to manually set the principal to the socket session, when the client connects (I have this information thanks to a custom cookie, and I don't use Spring security)?
If 1 is not possible, how to add additional attributes to the socket session when the client connects?
How to access the socket session and its attributes from a message handling method?
Is there a way to access the login and passcode sent by the browser at connection time. They seem to be completely ignore by Spring and not accessible.
UPDATE: With Spring 4.1 it is possible to set the user on the handshake for #1 from above. Per the Spring documentation you can create a new class which extends DefaultHandshakeHandler and override the determineUser method. Additionally you can also create a security filter which sets the principal as well if you have a token. I have implemented the second one myself and I include some sample code for both below.
For #2 and #3 I do not think that it is possible still. For #4 Spring intentionally ignores these per the documentation here.
SAMPLE CODE FOR DefaultHandshakeHandler SUBCLASS:
#Configuration
#EnableWebSocketMessageBroker
public class ApplicationWebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
public class MyHandshakeHandler extends DefaultHandshakeHandler {
#Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler,
Map<String, Object> attributes) {
// add your own code to determine the user
return null;
}
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/myEndPoint").setHandshakeHandler(new MyHandshakeHandler());
}
}
SAMPLE CODE FOR SECURITY FILTER:
public class ApplicationSecurityTokenFilter extends GenericFilterBean {
private final static String AUTHENTICATION_PARAMETER = "authentication";
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (servletRequest instanceof HttpServletRequest) {
// check to see if already authenticated before trying again
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
if ((existingAuth == null) || !existingAuth.isAuthenticated()) {
HttpServletRequest request = (HttpServletRequest)servletRequest;
UsernamePasswordAuthenticationToken token = extractToken(request);
// dump token into security context (for authentication-provider to pick up)
if (token != null) { // if it exists
SecurityContextHolder.getContext().setAuthentication(token);
}
}
}
filterChain.doFilter(servletRequest,servletResponse);
}
private UsernamePasswordAuthenticationToken extractToken( HttpServletRequest request ) {
UsernamePasswordAuthenticationToken authenticationToken = null;
// do what you need to extract the information for a token
// in this example we assume a query string that has an authenticate
// parameter with a "user:password" string. A new UsernamePasswordAuthenticationToken
// is created and then normal authentication happens using this info.
// This is just a sample and I am sure there are more secure ways to do this.
if (request.getQueryString() != null) {
String[] pairs = request.getQueryString().split("&");
for (String pair : pairs) {
String[] pairTokens = pair.split("=");
if (pairTokens.length == 2) {
if (AUTHENTICATION_PARAMETER.equals(pairTokens[0])) {
String[] tokens = pairTokens[1].split(":");
if (tokens.length == 2) {
log.debug("Using credentials: " + pairTokens[1]);
authenticationToken = new UsernamePasswordAuthenticationToken(tokens[0], tokens[1]);
}
}
}
}
}
return authenticationToken;
}
}
// set up your web security for the area in question
#Configuration
public class SubscriptionWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/myEndPoint**","/myEndPoint/**").and()
.addFilterBefore(new ApplicationSecurityTokenFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic() // leave this if you want non web browser clients to connect and add an auth header
.and()
.csrf().disable();
}
}
** NOTE: ** DO NOT declare your filter as a Bean. If you do then it will also be picked up (at least using Spring Boot) in the generic filters so it will fire on every request.
This is impossible for the time being (Spring 4.0). An issue has been opened (and considered) at Spring: https://jira.springsource.org/browse/SPR-11228