How to enable the existing spring security for a given Servlet? - java

I have a servlet deployed
Myservlet.java
#Configurable
public class MyServlet extends HttpServlet {
#Autowired
MyService service;
#Override
public void init(ServletConfig config) throws javax.servlet.ServletException{
super.init(config);
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) {
//Do something here
}
Now the security for this is enabled in web.xml as :
<security-constraint>
<web-resource-collection>
<web-resource-name>myServlet</web-resource-name>
<url-pattern>/myUrl/*</url-pattern>
<http-method>HEAD</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>user</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
<security-role>
<role-name>user</role-name>
</security-role>
But where this spring application deployed already has a spring security enabled via #EnableWebSecurity
The controllers deployed in the spring application are all correctly getting authenticated as expected. But the servlet is not authenticating with spring security. I believe what's mentioned in the is stopping it from authenticating.
How do i make the servlet work with Spring security ?
Edit 1:
Spring security configuration: (Note that this is not syntactically correct) but user/role and datasource are all correct in my code. It's working fine for other REST apis deployed in spring application
#Configuration
#EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
String user_query = "select user from userTable where id=9999";
String role_query = "select role from roleTable where id=6666";
logger.info("Using the following query for role : " + role_query);
auth.
jdbcAuthentication()
.dataSource(dataSource) //Datasource is injected to this class
.usersByUsernameQuery(user_query)
.passwordEncoder(passwordEncoder())
.authoritiesByUsernameQuery(role_query);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().hasRole("myrole")
.and()
.httpBasic();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(runAsAuthenticationProvider());
}
#Autowired
protected RunAsManager runAsManager() {
RunAsManagerImpl runAsManager = new RunAsManagerImpl();
runAsManager.setKey("MyRunAsKey");
return runAsManager;
}

Related

session timeout in spring boot + React

I am able to configure session timeout in web.xml, but after session timeout getting errors from react side.
using spring security and jwt token.
WebSecurity.java
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailServiceImpl userDetails;
#Autowired
JWTAuthenticationFilter jwtRequestFilter;
#Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetails).passwordEncoder(getPasswordEncoder());
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.GET, "/index*", "/static/**", "/*.js", "/*.json", "/*.ico", "/*.png")
.permitAll().antMatchers("/resources/**", "/login", "/", "/actuator").permitAll()
.antMatchers("/authenticate/**", "/identity/**").permitAll().anyRequest().authenticated().and().cors().and()
.exceptionHandling().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
web.xml
<session-config>
<session-timeout>1</session-timeout>
</session-config>
I tried sessionManagement().InvalidSessionURL("url") but on login it always redirect to invalid url only.
There is no api for login, we are loading from React directly.
Please let me know how to redirect to login page on session timeout ? and how to handle errors from react side ?

Keycloak with spring boot issue when deploying on tomcat

I have secured my a Spring application with KeyCloak using Spring Security Adapter, this works fine on my local machine, but when i deployed the WAR on tomcat and try to call the API, i get the following internal server error :
o.s.b.w.servlet.support.ErrorPageFilter : Forwarding to error page from request [/api/statutOperations]
due to exception [null]
java.lang.NullPointerException: null
at org.keycloak.adapters.KeycloakDeploymentBuilder.internalBuild(KeycloakDeploymentBuilder.java:57) ~[keycloak-adapter-core-10.0.2.jar:10.0.2]
at org.keycloak.adapters.KeycloakDeploymentBuilder.build(KeycloakDeploymentBuilder.java:202) ~[keycloak-adapter-core-10.0.2.jar:10.0.2]
at org.keycloak.adapters.springboot.KeycloakSpringBootConfigResolver.resolve(KeycloakSpringBootConfigResolver.java:39) ~[keycloak-spr
Did i miss something, or is my configuration wrong, below is the necessary config :
Keycloak Config :
#Configuration
public class KeycloakConfig {
#Bean
KeycloakSpringBootConfigResolver configResolver() {
return new KeycloakSpringBootConfigResolver();
}
#Bean
KeycloakRestTemplate keycloakRestTemplate(KeycloakClientRequestFactory keycloakClientRequestFactory) {
return new KeycloakRestTemplate(keycloakClientRequestFactory);
}
}
#KeycloakConfiguration
public class KeycloakSpringSecuriteConfig extends KeycloakWebSecurityConfigurerAdapter {
#Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http); http.authorizeRequests().antMatchers("/api/**").authenticated().anyRequest().permitAll();
}
}
application.properties :
keycloak.realm=cirta
keycloak.auth-server-url=http://localhost:8085/auth
keycloak.resource=cirta-api
keycloak.public-client=true
keycloak.cors=true
keycloak.ssl-required=external
I also added the following context.xml keycloak.json and web.xml in META-INF and WEB-INF directories :
context.xml
<Context path="/cirtaapi">
<Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
</Context>
keycloak.json
{
"realm" : "cirta",
"resource" : "cirta-api",
"auth-server-url" : "https://localhost:8085/auth",
"ssl-required" : "external",
"enable-cors" : true
}
web.xml
<module-name>cirtaapi</module-name>
<security-constraint>
<web-resource-collection>
<web-resource-name>Operations</web-resource-name>
<url-pattern>/api/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>app-manager</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>KEYCLOAK</auth-method>
<realm-name>cirta</realm-name>
</login-config>
<security-role>
<role-name>app-manager</role-name>
</security-role>
This has been fixed in keycloak 11.0.0. Similar question is out there to describe this: NPE when loading custom SecurityConfig for Keycloak in WebMvcTest and provide a workaround for version 9.0.1 to 10.
See also: https://github.com/gtiwari333/spring-boot-web-application-seed/blob/master/main-app/src/main/java/gt/app/config/security/SecurityConfig.java

Spring Security for Rest #PreAuthorize 500 error

I want basic security for rest ,this is my config:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private PacijentUserDetailsService pacijent;
#Autowired
private FizioterapeutUserDetailsService fizioterapeut;
#Autowired
private FizijatarUserDetailsService fizijatar;
#Override
protected void configure(AuthenticationManagerBuilder
auth) throws Exception {
auth.userDetailsService(pacijent)
.passwordEncoder(new
BCryptPasswordEncoder());
auth.userDetailsService(fizioterapeut).passwordEncoder(new
BCryptPasswordEncoder());
auth.userDetailsService(fizijatar).passwordEncoder(new
BCryptPasswordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/pacijent/", "/fizijatar/","/fizioterapeut/").permitAll()
.antMatchers("/pacijent/**","/fizijatar/**","/fizioterapeut/**").authenticated()
.and()
.httpBasic()
.realmName("Ordinacija")
.and()
.csrf()
.disable();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
I have 3 implements of userdetailservice this is one example:
#Component
public class PacijentUserDetailsService implements UserDetailsService {
#Autowired
private PacijentService pacijentService;
#Override
public UserDetails loadUserByUsername(String jmbg) throws UsernameNotFoundException {
Pacijent pacijent = pacijentService.vratiPacijenta(jmbg);
if (pacijent == null) {
throw new UsernameNotFoundException(String.format("Pacijent nije pronadjen", jmbg));
}
List<GrantedAuthority> authorities = new ArrayList<>();
if (pacijentService.postojiPacijentPoJmbgu(jmbg)) {
authorities = AuthorityUtils.createAuthorityList("ROLE_USER");
}
UserDetails userDetails = new org.springframework.security.core.userdetails.User(pacijent.getJmbg(),
pacijent.getSifra(), authorities);
return userDetails;
}
}
And my web xml file:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</context-param>
org.springframework.web.context.ContextLoaderListener
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet- class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
When i start my app and go to rest method which have #PreAuthorize method i have error 500:
Request processing failed; nested exception is org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContex.
i write the answer to help others.
If you have a Filter like OncePerRequestFilter where you check authentication, so when you check for authentication and it fails you can set this:
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
Otherwise if you let manage authentication to spring you can use the exception handler:
#ControllerAdvice
#RestController
public class CustomExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler({AccessDeniedException.class})
public final
ResponseEntity<Object> handleUserNotFoundException(EntityNotFoundException ex, WebRequest request){
return new ResponseEntity<>("Unauthorized", HttpStatus.UNAUTHORIZED);
}
}
Try to change your configuration something like this.
.authorizeRequests()
.antMatchers(permitAllEndpointList.toArray(new String[permitAllEndpointList.size()]))
.permitAll()
.and()
.authorizeRequests()
.antMatchers(API_ROOT_URL).authenticated()
I hope this solves your issue.

Implement a custom ServerAuthModule for JBoss

I need to remember the original URL of the Http Request, then redirect this request to a web form for a user authentication. In case of a successful authentication, the user must be redirected to the original URL just remembered above.
I am using JBoss 7.1.1 Final, a standard web.xml, and the JBoss Login Module org.jboss.security.auth.spi.DatabaseServerLoginModule:
I had referred the following links which didn't answer my question completely:
Precedence of security-constraint over filters in
Servlets
Jaspic ServerAuthModule delegating to JAAS Krb5LoginModule
Implementing container authentication in Java EE with JASPIC
Oracle GlassFish Server 3.0.1 Application Development Guide
However, after impltementing my solution, my custom ServerAuthModule is not called at all. What is even worse, I did not get any HttpResponse from the server. Something got broken, please help!
My web.xml:
<security-constraint>
<web-resource-collection>
<web-resource-name>All resources in /pages/*</web-resource-name>
<description>All resources in /pages/*</description>
<url-pattern>/pages/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>general</role-name>
</auth-constraint>
</security-constraint>
<security-constraint>
<display-name>Restrict direct access to the /resources folder.</display-name>
<web-resource-collection>
<web-resource-name>The /resources folder.</web-resource-name>
<url-pattern>/resources/*</url-pattern>
</web-resource-collection>
<auth-constraint />
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.jsf</form-login-page>
<form-error-page>/loginFailed.jsf</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>general</role-name>
</security-role>
My jboss-web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<security-domain>jBossJaasMysqlRealm</security-domain>
<valve>
<class-name>org.jboss.as.web.security.jaspi.WebJASPIAuthenticator</class-name>
</valve>
</jboss-web>
My standalone.xml:
<security-domain name="jBossJaasMysqlRealm" cache-type="default">
<authentication-jaspi>
<login-module-stack name="lm-stack">
<login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required">
<module-option name="dsJndiName" value="java:/MySqlDS_IamOK"/>
<module-option name="principalsQuery" value="select password from user where username=?"/>
<module-option name="rolesQuery" value="select role, 'Roles' from user_role where username=?"/>
</login-module>
</login-module-stack>
<auth-module code="at.alex.ok.web.utils.RequestMarkerServerAuthModule" login-module-stack-ref="lm-stack"/>
</authentication-jaspi>
</security-domain>
My custom WebServerAuthModule:
import org.jboss.as.web.security.jaspi.modules.WebServerAuthModule;
public class RequestMarkerServerAuthModule extends WebServerAuthModule {
public static final String ORIGINAL_URL = "originalURL";
protected static final Class[] supportedMessageTypes = new Class[] {
HttpServletRequest.class, HttpServletResponse.class };
public void initialize(MessagePolicy reqPolicy, MessagePolicy resPolicy,
CallbackHandler cBH, Map opts) throws AuthException {
System.out.println( this.getClass().getName() + ".initialize() called");
}
public Class[] getSupportedMessageTypes() {
return supportedMessageTypes;
}
public AuthStatus validateRequest(MessageInfo msgInfo, Subject client,
Subject server) throws AuthException {
try {
System.out.println( this.getClass().getName() + ".validateRequest() called");
processAuthorizationToken(msgInfo, client);
return AuthStatus.SUCCESS;
} catch (Exception e) {
AuthException ae = new AuthException();
ae.initCause(e);
throw ae;
}
}
private void processAuthorizationToken(MessageInfo msgInfo, Subject s)
throws AuthException {
HttpServletRequest request = (HttpServletRequest) msgInfo
.getRequestMessage();
String originalURL = request.getRequestURL().toString();
request.getSession().setAttribute(ORIGINAL_URL, originalURL);
}
public AuthStatus secureResponse(MessageInfo msgInfo, Subject service)
throws AuthException {
System.out.println( this.getClass().getName() + ".secureResponse() called");
return AuthStatus.SEND_SUCCESS;
}
public void cleanSubject(MessageInfo msgInfo, Subject subject)
throws AuthException {
System.out.println( this.getClass().getName() + ".cleanSubject() called");
}
}
This question is put incorectly, because:
For a redirect to the originally requested URL after a successfull login, there is no need to implement a custom ServerAuthModule for JBoss.
The interface javax.servlet.RequestDispatcher has the constant FORWARD_REQUEST_URI, which denotes the name of the Http-Request attribute under which the original request URI is made available to the processor of the forwarded request.
Using JSF 2.2 and a View-Scoped backing bean LoginBean, my solution is simply to obtain the originally requested URL in a #PostConstruct method of the backing bean, and store it in a session attribute, as follows:
#ManagedBean(name="loginBean")
#ViewScoped
public class LoginBean {
private String originalURL;
#PostConstruct
private void init() {
ExternalContext extCtx = FacesContext.getCurrentInstance().getExternalContext();
String origURL = (String) extCtx.getRequestMap().get(RequestDispatcher.FORWARD_REQUEST_URI);
HttpServletRequest request = (HttpServletRequest) extCtx.getRequest();
HttpSession session = (HttpSession)extCtx.getSession(false);
if (session == null){
session = (HttpSession)extCtx.getSession(true);
}
if (origURL!=null && session.getAttribute(ORIGINAL_URL) == null){
String applicationName = request.getContextPath();
origURL = origURL.substring(applicationName.length(), origURL.length());
session.setAttribute(ORIGINAL_URL, origURL);
}
}
Then, in the login() method of the same backing bean, redirect the user to the originally requested URL in case of a successfull log-in like this:
public String login() {
HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest();
try {
request.login(this.getLogin(), this.getPassword());
} catch (ServletException e) {
// handle bad username / password here
}
return this.originalURL + "?faces-redirect=true";
}

Both isAnonymous() and isAuthenticated() are returning false

I have a simple page which is displays simple text depending on whether the user is logged in or not.
<sec:authorize access="isAnonymous()">
No, you failed!
</sec:authorize>
<sec:authorize access="isAuthenticated()">
yes, logged in. Well done!
</sec:authorize>
The above code displays nothing! which means both isAuthenticated() and isAnonymous() have returned false.
It is suggested here (Both isAnonymous() and isAuthenticated() return false on error page) that I must use this configuration for my filter mapping:
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<!-- apply Spring Security authentication to error-pages -->
<dispatcher>ERROR</dispatcher>
</filter-mapping>
I am not using XML, but my configuration is the same:
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
characterEncoding.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
FilterRegistration.Dynamic security = servletContext.addFilter("springSecurityFilterChain", new DelegatingFilterProxy());
security.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
Why else would this happen?
Edit:
This is my security context:
#Configuration
#EnableWebSecurity
public class SecurityContext extends WebSecurityConfigurerAdapter {
#Autowired
private UserRepository userRepository;
#Override
public void configure(WebSecurity web) throws Exception {
web
//Spring Security ignores request to static resources such as CSS or JS files.
.ignoring()
.antMatchers("/static/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
//Configures form login
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login/authenticate")
.failureUrl("/login?error=bad_credentials")
//Configures the logout function
.and()
.logout()
.deleteCookies("JSESSIONID")
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
//Configures url based authorization
.and()
.authorizeRequests()
//Anyone can access the urls
.antMatchers(
"/auth/**",
"/login",
"/signin/**",
"/signup/**",
"/user/register/**"
).permitAll()
//The rest of the our application is protected.
.antMatchers("/**").hasRole("USER")
//Adds the SocialAuthenticationFilter to Spring Security's filter chain.
.and()
.apply(new SpringSocialConfigurer());
}
/**
* Configures the authentication manager bean which processes authentication
* requests.
*/
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
/**
* This is used to hash the password of the user.
*/
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
/**
* This bean is used to load the user specific data when social sign in
* is used.
*/
#Bean
public SocialUserDetailsService socialUserDetailsService() {
return new SimpleSocialUserDetailsService(userDetailsService());
}
/**
* This bean is load the user specific data when form login is used.
*/
#Bean
public UserDetailsService userDetailsService() {
return new RepositoryUserDetailsService(userRepository);
}
}
This the page controller:
#Controller
public class LoginController {
private static final Logger LOGGER = LoggerFactory.getLogger(LoginController.class);
protected static final String VIEW_NAME_LOGIN_PAGE = "user/login";
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String showLoginPage() {
LOGGER.debug("Rendering login page.");
return VIEW_NAME_LOGIN_PAGE;
}
}
Make sure you are not bypassing security for that URL like as follows:
<http pattern="/xyz.xx" security="none" />
The ApplicationContext must contain
#ComponentScan(basePackages = {
"com.social.user.detail.service"
})
In this package I have the my user class containing the following:
public interface UserService {
public User registerNewUserAccount(RegistrationForm userAccountData) throws DuplicateEmailException;
}

Categories

Resources