spring security weird redirection - redirect

I have this spring security configuration and the controller is the one I show. Its really simply... The /form loads the form and the others are JSP that only load a page with a string like the filename.
I am having a weird behaviour I havent experienced before like:
If I hit http://localhost:1111/preferences, it redirects to /form but it shows a 404.Other scenario I faced yesterday is that I login correctly (At least at code level), but it redirects to /home but doesnt show the correct screen.
]Does anyone know what is this all about ?.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/form").permitAll()
.antMatchers("/history").hasAuthority("EMPLOYEE")
.antMatchers("/home").hasAuthority("EMPLOYEE")
.antMatchers("/live").hasAuthority("EMPLOYEE")
.antMatchers("/management").hasAnyAuthority("ADMIN")
.antMatchers("/preferences").hasAuthority("EMPLOYEE")
.antMatchers("/api/**").hasAuthority("EMPLOYEE")
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/form")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/home", true)
.usernameParameter("username")
.passwordParameter("password")
.failureHandler(new CustomAuthenticationFailureHandler());
}
and
#GetMapping(value = {"/form"})
public ModelAndView login() {
return new ModelAndView("form");
}
#GetMapping(value = {"/history"})
public ModelAndView history() {
return new ModelAndView("history");
}
#GetMapping(value = {"/home"})
public ModelAndView home() {
return new ModelAndView("home");
}
#GetMapping(value = {"/live"})
public ModelAndView live() {
return new ModelAndView("live");
}
#GetMapping(value = {"/management"})
public ModelAndView management() {
return new ModelAndView("management");
}
#GetMapping(value = {"/preferences"})
public ModelAndView preferences() {
return new ModelAndView("preferences");
}

Related

Spring boot api gives 403 forbidden error

I have a spring boot application with a mongo databse and spring security as a dependency.
It has two services first one for authentication and second one for application resource (entities, services controllers).
This is my config class in the authentication service:
#Configuration
#EnableWebSecurity
public class AuthServerSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
#Bean
protected UserDetailsService userDetailsService() {
return new MongoUserDetailsService();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().anyRequest().authenticated();
System.out.println("auth");
}
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
#Bean(name="authenticationManager")
#Lazy
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
this is the rest controller:
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
#RequestMapping(value = "/api/users")
public class UserController {
#Autowired
UserServiceImpl userServiceImpl;
//Getting all users
#GetMapping(value = "")
public List<UserDTO> getAllUsers() {
return userServiceImpl.getAllUsers();
}
//Getting a user by ID
#GetMapping(value = "/profil/{userId}")
public UserDTO getUserById(#PathVariable String userId) {
return userServiceImpl.getUserById(userId);
}
//Getting a user by Username
#GetMapping(value = "/profil/username/{username}")
public UserDTO getUserByUsernameOrEmail(String username) {
return userServiceImpl.getUserByUsernameOrEmail(username);
}
//Logout user and delete token
#PostMapping("/logout")
public void logout(HttpServletRequest request) {
userServiceImpl.logout(request);
}
I changed my configure method to this :
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests() // authorize
.anyRequest().authenticated() // all requests are authenticated
.and()
.httpBasic();
http.cors();
}
Now i get 401 unauthorized when acceccing protected resources.The problem is now even when i send the correct bearer token in the request header i still get 401 unauthorized "Full authentication is required to access this resource"
Update:
I changed my project architecture from microservices to one simple spring boot project.
this is the new code of the class "AuthServerSecurityConfig"
#Configuration
public class AuthServerSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
#Bean
protected UserDetailsService userDetailsService() {
return new MongoUserDetailsService();
}
#Autowired
BCryptPasswordEncoder passwordEncoder;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//auth.userDetailsService(userDetailsService());
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll().and()
.httpBasic();
http.cors();
}
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
#Bean(name="authenticationManager")
#Lazy
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
and this "ResourceServerConfig" code:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Autowired private ResourceServerTokenServices tokenServices;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("foo").tokenServices(tokenServices);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() // authorize
.antMatchers("/oauth/**").permitAll();
http
.authorizeRequests().antMatchers("/api/**").authenticated();
http
.headers().addHeaderWriter(new HeaderWriter() {
#Override
public void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
response.addHeader("Access-Control-Allow-Origin", "*");
if (request.getMethod().equals("OPTIONS")) {
response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method"));
response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
}
}
});
}
}
When i try to access protected resource i get "error": "unauthorized",
"error_description": "Full authentication is required to access this resource", Which is the normal behaviour.The problem is now i can't login to get the user aceess_token.
I get "401 unauthorized" when accessing this endpoint "http://localhost:8080/oauth/token?grant_type=password&username=user&password=user".
These are the default init user credentials and the user exists in my mongodatabase with a crypted and correct format password starts with "$2a" and has "60" caracters.
I get in "Encoded password does not look like BCrypt
Authentication failed: password does not match stored value" in the console when trying to login.
In ResourceServerConfig class file, in the configure method change to the below code.
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll().and()
.httpBasic();
Let me know if it worked.
Here's an example of configuration spring security with a jwt token check :
you can change the data source from h2 to mongodb and find the filters and providers used in my repo :
https://github.com/e2rabi/sbs-user-management/tree/main/sbs-user-management
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled=true)
#FieldDefaults(level = PRIVATE, makeFinal = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
// Put your public API here
new AntPathRequestMatcher("/public/**"),
new AntPathRequestMatcher("/h2-console/**"),
);
private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);
TokenAuthenticationProvider provider;
SecurityConfig(final TokenAuthenticationProvider provider) {
super();
this.provider = requireNonNull(provider);
}
#Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(provider);
}
#Override
public void configure(final WebSecurity web) {
web.ignoring().requestMatchers(PUBLIC_URLS);
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(STATELESS)
.and()
.exceptionHandling()
// this entry point handles when you request a protected page and you are not yet
// authenticated
.defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
.and()
.authenticationProvider(provider)
.addFilterBefore(restAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.requestMatchers(PROTECTED_URLS)
.authenticated()
.and()
.csrf().disable()
.cors().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
// h2 console config
http.headers().frameOptions().sameOrigin();
// disable page caching
http.headers().cacheControl();
}
#Bean
TokenAuthenticationFilter restAuthenticationFilter() throws Exception {
final TokenAuthenticationFilter filter = new TokenAuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
#Bean
SimpleUrlAuthenticationSuccessHandler successHandler() {
final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
successHandler.setRedirectStrategy(new NoRedirectStrategy());
return successHandler;
}
#Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
FilterRegistrationBean disableAutoRegistration(final TokenAuthenticationFilter filter) {
final FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
#Bean
AuthenticationEntryPoint forbiddenEntryPoint() {
return new HttpStatusEntryPoint(FORBIDDEN);
}
}
Hope that my answer can help,
you can drop a breakpoint to the line change the response status, and then check who and why it returns 403, it can finally help you get the solution
Drop a breakpoint on the line set the 403 status, to see how this happen from the stackframes.
Guess that it returns 403 without much other information, but it must need to set the status to the response, right? So drop a breakpoint to the setStatus method, I don't know where it should locate, in tomcat lib, spring lib, or servlet lib. Check the HttpResponse, they're several implementation, set the breakpoints for those setStatus/setCode methods. (Next you can see it acutally happens at HttpResponseWrapper::setStatus)
Analyze the stackframes to see what's going on there
please check https://stackoverflow.com/a/73577697/4033979

call to authenticated rest web service fails in case of post method spring boot

I'm trying to consume a rest web service which is authenticated in spring boot application where the Httpmethod is POST,
Below I would like to show how all set up work to consume authenticated web service for HttpMethod.GET and then what changes I try to consume same authenticated web service for HttpMethod.POST and throws 401 Unauthorized ERROR,
RestTemplateFactory to get restTemplte,
public class RestTemplateFactory implements FactoryBean<RestTemplate>, InitializingBean {
#Autowired
private RestTemplate restTemplate;
public RestTemplate getObject() {
return restTemplate;
}
public Class<RestTemplate> getObjectType() {
return RestTemplate.class;
}
public boolean isSingleton() {
return true;
}
public void afterPropertiesSet() {
HttpHost host = new HttpHost("localhost", 9090, "http");
restTemplate = new RestTemplate(
new HttpComponentsClientHttpRequestFactoryBasicAuth(host));
}
}
For basic authentication,
public class HttpComponentsClientHttpRequestFactoryBasicAuth extends HttpComponentsClientHttpRequestFactory {
HttpHost host;
public HttpComponentsClientHttpRequestFactoryBasicAuth(HttpHost host) {
super();
this.host = host;
}
protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
return createHttpContext();
}
private HttpContext createHttpContext() {
AuthCache authCache = new BasicAuthCache();
BasicScheme basicAuth = new BasicScheme();
authCache.put(host, basicAuth);
BasicHttpContext localcontext = new BasicHttpContext();
localcontext.setAttribute(HttpClientContext.AUTH_CACHE, authCache);
return localcontext;
}
}
Calling a authenticated web service for HttpMethod.Get method,
public ResponseEntity<SomeResponse> consumeRestApi(SomeRequest request) throws InvalidDataException {
ResponseEntity<SomeResponse> responseEntity = null;
try {
RestTemplate restTemplate = restTemplateFactory.getRestTemplate();
restTemplate
.getInterceptors()
.add(new BasicAuthorizationInterceptor(username, pwd));
responseEntity = restTemplate.exchange("http://localhost:9090/sendMail?phone=60598745&email=abc#gmail.com", HttpMethod.GET, null, SomeResponse.class);
} catch (Exception e) {
// Exception handing...
}
return responseEntity;
}
And I do have dummy server running at localhost, with HttpMethod.Get and this is the authenticated service I'm trying to consume in above set up,
#RequestMapping(value = "/sendMail", method = RequestMethod.GET)
public ResponseEntity<SomeResponse> sendmail(#RequestParam String phone, #RequestParam String email){
SomeResponse response = SomeResponse.builder()
.id("101")
.type("formdata")
.fieldValues(getFieldValues(phone,email))
.build();
return new ResponseEntity<>(response,HttpStatus.CREATED);
}
When its a HttpMethod.GET method it works perfectly fine with all the set up mentioned above,
Now, I want to change the same web service to be consumed, to accept a HttpMethod.POST
So below are the changes I tried out but it throw back an error of 401 i.e. Unauthorized error
The changes I try for post method,
By keeping the RestTemplateFactory and HttpComponentsClientHttpRequestFactoryBasicAuth same
I first change, the rest api on dummy server to accept request with POST so,
#RequestMapping(value = "/sendMail", method = RequestMethod.POST)
public ResponseEntity<SomeResponse> sendmail(#RequestParam String phone, #RequestParam String email){
// Same as above
}
Next change is calling method with method post,
public ResponseEntity<SomeResponse> consumeRestApi(SomeRequest request) throws InvalidDataException {
ResponseEntity<SomeResponse> responseEntity = null;
try {
RestTemplate restTemplate = restTemplateFactory.getRestTemplate();
restTemplate
.getInterceptors()
.add(new BasicAuthorizationInterceptor(username, pwd));
SomeResponse response = restTemplate.postForObject("http://localhost:9090/sendMail?phone=60598745&email=abc#gmail.com",request, SomeResponse.class);
} catch (Exception e) {
// Exception handling....
}
}
return new ResponseEntity<SomeResponse>(HttpStatus.CREATED);
}
Does anyone has any suggestion where I'm going wrong with this,
Thanks in advance.
I guess the issue lies in your way of creating and configuring the RestTemplate. Spring Boot provides the RestTemplateBuilder to construct a RestTemplate and it has builder methods to do additional configuration.
In addition the RestTemplate is thread safe so instead of re-recreating it to use it you can reuse the created instance. That being said your calling class can be refactored to something like this
public class EndpointTester {
private final RestTemplate rest;
public EndpointTester(RestTemplateBuilder rtb, String username, String pwd) {
this.rest = rtb.basicAuthorization(username, pwd).build();
}
public ResponseEntity<SomeResponse> consumeRestApi(SomeRequest request) throws InvalidDataException {
ResponseEntity<SomeResponse> responseEntity = null;
try {
responseEntity = rest.postForEntity("http://localhost:9090/sendMail?phone=60598745&email=abc#gmail.com", null, SomeResponse.class);
} catch (Exception e) {
// Exception handing...
}
return responseEntity;
}
}
This way you don't need your RestTemplateFactory and HttpComponentsClientHttpRequestFactoryBasicAuth which simplifies your configuration and code.

Secure Rest API Spring MVC

been trying to implement spring security to REST api but they work even without the username & password
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static String REALM="MY_TEST_REALM";
#Autowired
RestAuthenticationEntryPoint restAuthenticationEntryPoint;
#Autowired
public void ConfigureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("admin").password("admin").roles("USER", "ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/user").hasRole("ADMIN")
.and().httpBasic().realmName(REALM).authenticationEntryPoint(gEntryPoint())
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public RestAuthenticationEntryPoint gEntryPoint() {
return new RestAuthenticationEntryPoint();
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/*");
}
}
Rest authetication entry point
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{
#Override
public void commence(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
}
}
Rest Controller
#RestController
#RequestMapping(value = "/dray")
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class dray {
#RequestMapping(value = "/user", method = RequestMethod.GET)
#ResponseBody
public Auser getUser() {
return new Auser("john", "carter");
}
}
requests to jsp pages work fine, if user is not authenticated, he's redirected to the login form of spring security but rest api work without even asking for credentials, so how to send response 401 unauthorized if api used doesn't have the credentials?

Circular view path exception with Spring framework And AngularJs

I am relatively new to the Spring boot frame work. I had a basic web application built in Angular with Spring boot connected and to a Mongodb. The application allowed users to add todo lists and register for the the website. When the application started it returned the todolists stored in mongodb to the view. The user could register, and there details were stored in a Mongo repository.
When I added and implemented spring security I got the error message
Circular view path [login]: would dispatch back to the current handler URL [/login] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
What I want to happen is, when the webapp loads, I want the index.html to be injected with todo.html. Then if a user logs in they will be directed to another page or some Ui feature to become available. At the moment I am stuck in this Circular view pathloop.
I have looked through the different answers but I still am confused as to what exactly is causing the issue. I believe it is in the WebSecurityConfig class
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
UserDetailsService userDS;
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.authorizeRequests()
.antMatchers("/api/todos/*").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDS);
}
#Override
protected UserDetailsService userDetailsService() {
return userDS;
}
}
AuthUserDetailsService
#Repository
public class AuthUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository users;
private org.springframework.security.core.userdetails.User userdetails;
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
// TODO Auto-generated method stub
boolean enabled = true;
boolean accountNonExpired = true;
boolean credentialsNonExpired = true;
boolean accountNonLocked = true;
todoapp.models.User user = getUserDetail(username);
userdetails = new User (user.getUsername(),
user.getPassword(),
enabled,
accountNonExpired,
credentialsNonExpired,
accountNonLocked,
getAuthorities(user.getRole())
);
return userdetails;
}
public List<GrantedAuthority> getAuthorities(Integer role) {
List<GrantedAuthority> authList = new ArrayList<GrantedAuthority>();
if (role.intValue() == 1) {
authList.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
} else if (role.intValue() == 2) {
authList.add(new SimpleGrantedAuthority("ROLE_USER"));
}
return authList;
}
private todoapp.models.User getUserDetail(String username){
todoapp.models.User user = users.findByUsername(username);
return user;
}
}
TodoController
#RestController
#RequestMapping("/api/todos")
public class TodoController {
#Autowired
TodoRepository todoRepository;
#RequestMapping(method=RequestMethod.GET)
public List<Todo> getAllTodos() {
return todoRepository.findAll();
}
#RequestMapping(method=RequestMethod.POST)
public Todo createTodo(#Valid #RequestBody Todo todo) {
return todoRepository.save(todo);
}
#RequestMapping(value="{id}", method=RequestMethod.GET)
public ResponseEntity<Todo> getTodoById(#PathVariable("id") String id) {
Todo todo = todoRepository.findOne(id);
if(todo == null) {
return new ResponseEntity<Todo>(HttpStatus.NOT_FOUND);
} else {
return new ResponseEntity<Todo>(todo, HttpStatus.OK);
}
}
#RequestMapping(value="{id}", method=RequestMethod.PUT)
public ResponseEntity<Todo> updateTodo(#Valid #RequestBody Todo todo, #PathVariable("id") String id) {
Todo todoData = todoRepository.findOne(id);
if(todoData == null) {
return new ResponseEntity<Todo>(HttpStatus.NOT_FOUND);
}
todoData.setTitle(todo.getTitle());
todoData.setCompleted(todo.getCompleted());
Todo updatedTodo = todoRepository.save(todoData);
return new ResponseEntity<Todo>(updatedTodo, HttpStatus.OK);
}
#RequestMapping(value="{id}", method=RequestMethod.DELETE)
public void deleteTodo(#PathVariable("id") String id) {
todoRepository.delete(id);
}
}
RecourceController
#Configuration
public class ResourceController extends WebMvcConfigurerAdapter{
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/api/todos").setViewName("home");
registry.addViewController("/register").setViewName("register");
registry.addViewController("/login").setViewName("login");
}
}
Any help will be greatly appreciated.
This is the Project Layout.
You forgot to add .html to your view names:
registry.addViewController("/").setViewName("app/views/index.html");
registry.addViewController("/api/todos").setViewName("app/views/home.html");
registry.addViewController("/register").setViewName("app/views/register.html");
registry.addViewController("/login").setViewName("app/views/login.html");
Spring Boot registers a ResourceHttpRequestHandler which is capable of resolving static resources under static folder.
Because you set login as view name ResourceHttpRequestHandler tries to load static/login which apparently does not exist.
Change it to app/views/login.html so that static/login becomes static/app/views/login.html.

No mapping found for HTTP request with URI

I have Spring Boot application, everything works fine until I implement spring security in front of my application. This is a RESTful api that has a token based authentication. What's even more weird it works (!) intermittently - by intermittently I mean restarting the application will return the right responses such as 401/403 if unauthenticated and other codes if user is authorized to access them. This is being deployed into WebLogic.
2017-01-05 14:12:51.164 WARN 11252 --- [ (self-tuning)'] o.s.web.servlet.PageNotFound : No mapping found for HTTP request with URI [/user] in DispatcherServlet with name 'dispatcherServlet'
WebApplication.java
#SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class WebApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String[] args) {
Object[] sources = new Object[2];
sources[0] = WebConfiguration.class;
sources[1] = WebSecurityConfiguration.class;
SpringApplication.run(sources, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WebApplication.class);
}
}
WebConfiguration.java
#Configuration
#ComponentScan(basePackages = { "com.controller", "com.service", "com.dao"})
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class })
public class WebConfiguration extends WebMvcConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(WebConfiguration.class);
/**
* Setup a simple strategy: use all the defaults and return XML by default
* when not sure.
*/
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.defaultContentType(MediaType.APPLICATION_JSON).mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML);
}
#Bean(name = "entityManagerFactory")
public EntityManagerFactory getQmsEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName(Config.PERSISTENCE_UNIT_NAME);
em.setPersistenceXmlLocation("META-INF/persistence.xml");
em.setDataSource(getDataSource());
em.setJpaVendorAdapter(getJpaHibernateVendorAdapter());
em.afterPropertiesSet();
return em.getObject();
}
#Bean
public HibernateJpaVendorAdapter getJpaHibernateVendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(true);
// adapter.setDatabase("ORACLE");
adapter.setDatabasePlatform("org.hibernate.dialect.Oracle10gDialect");
return adapter;
}
#Bean(name="dataSource", destroyMethod = "")
//http://stackoverflow.com/questions/19158837/weblogic-datasource-disappears-from-jndi-tree
#Qualifier("dataSource")
#Profile("weblogic")
public DataSource dataSource() {
DataSource dataSource = null;
JndiTemplate jndi = new JndiTemplate();
try {
dataSource = (DataSource) jndi.lookup("jdbc/datasource");
} catch (NamingException e) {
logger.error("NamingException for jdbc/datasource", e);
}
return dataSource;
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
}
};
}
}
WebSecurityConfiguration.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#ComponentScan({
"com.subject",
"com.custom"
})
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private StatelessAuthenticationFilter statelessAuthenticationFilter;
#Autowired
private RestAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private CusAuthenticationProvider cusAuthenticationProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(cusAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.securityContext()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.addFilterBefore(statelessAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler);
}
}
StatelessAuthenticationFilter.java
#Component
public class StatelessAuthenticationFilter extends OncePerRequestFilter {
#Inject
private SubjectLookupService subjectLookupService;
#Override
public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
SecurityContextHolder.getContext().setAuthentication(authenticateUser(request));
filterChain.doFilter(request, response);
}
private Authentication authenticateUser(HttpServletRequest request) {
try {
String application = StringUtils.defaultString(request.getParameter("application"));
UserInfo me = subjectLookupService.getUserInfo();
List<GrantedAuthority> roles = me.getRoles().stream()
.map(role -> new SimpleGrantedAuthority("ROLE_" + role.getName())).collect(Collectors.toList());
UserDetails user = new User(me.getUsername(), "", roles);
Authentication authentication = new UserAuthentication(user);
return authentication;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Controller.java
#RestController
public class Controller {
#Autowired
private QService qService;
#PreAuthorize("hasAnyRole('view', 'admin')")
#RequestMapping(value = "/q/{year}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<?> listQuotas(#PathVariable Integer year) {
return new ResponseEntity<>(qService.listQs(year), HttpStatus.OK);
}
#RequestMapping(value = "/user", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<?> user(HttpServletRequest request) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
return new ResponseEntity<>( auth.getPrincipal(), HttpStatus.OK);
}
#PreAuthorize("hasRole('shouldntauthorize')")
#RequestMapping(value = "/unauthorized/{year}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<?> unauthorized(#PathVariable Integer year) {
return new ResponseEntity<>(qService.listQs(year), HttpStatus.OK);
}
}
When it works - I am able to hit any of the above methods using HTTP gets and I am getting correct responses. When it's not working, I am constantly getting:
2017-01-05 14:18:47.506 WARN 11252 --- [ (self-tuning)'] o.s.web.servlet.PageNotFound : No mapping found for HTTP request with URI [/user] in DispatcherServlet with name 'dispatcherServlet'
I can verify in the logs that when Spring Boot initializes the application is also sets the correct mapping URL.
Any ideas what could be the problem here?
when you say "intermittently" I tend to think that the problem is with Spring startup configuration.
So, I'd be weary on the fact that you have #ComponentScan twice, and with different packages.
Could you try removing
#ComponentScan(basePackages = { "com.controller", "com.service", "com.dao"})
from class WebConfiguration.java and
#ComponentScan({ "com.subject", "com.custom" })
from class WebSecurityConfiguration.java, and replace them with a single
#ComponentScan(basePackages = { "com.controller", "com.service", "com.dao", "com.subject", "com.custom"})
in the main SpringBoot class?