need your help here please.
I have a login page. After I enter username/password, I want to see Dashboard page. But I am getting 404 page not found. Can anyone please tell what is going on here.
When I hit http://localhost:8080 -> It goes to http://localhost:8080/login - Which is expected.
After I enter username/password, it goes to http://localhost:8080 - Expected: to go to Dashboard page i.e. http://localhost:8080/dashboard
#Component
public class SimpleAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
...
redirectStrategy.sendRedirect(request, response, "dashboard");
}
DashboardController.java
#Controller
#RequestMapping("/dashboard")
public class DashboardController {
#RequestMapping("/")
String init(){
System.out.println("Dashboard - init()");
return "dashboard_init";
}
}
app.component.html
Hello... {{name}}
Hello... {{title}}
<h1>
Welcome {{title}}!
</h1>
<p>Id: <span>{{greeting.id}}</span></p>
<p>Message: <span>{{greeting.content}}!</span></p>
app-routing.module.ts
import {DashboardComponent} from "./dashboard/dashboard.component";
const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
// { path: 'dashboard', redirectTo: '/dashboard', pathMatch: 'full' },
{
path: 'dashboard_init',
component: DashboardComponent,
data: { title: 'Dashboard' }
}
];
dashboard.component.ts
#Component({
selector: 'dashboard-component',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.css'],
})
export class DashboardComponent implements OnInit {
private currentAssociate: Associate;
constructor(private http: Http,
private router: Router) {
}
ngOnInit(): void {
// initialize services and data
this.http
.get('/dashboard')
.toPromise()
.then(response => {
let data = response.json();
if (data.currentAssociate) this.currentAssociate = data.currentAssociate as Associate;
})
.catch(error => {
// this.alertService.error(error);
});
}
}
dashboard.component.html
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head>
<meta charset="utf-8" />
<title>Dashboard</title>
</head>
<div>
<B>Dashboard...</B>
</div>
</html>
Error: (When the url is http://localhost:8080/dashboard/)
Dashboard - init()
[2m2018-03-26 10:07:20.421[0;39m [31mERROR[0;39m [35m13184[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36morg.thymeleaf.TemplateEngine [0;39m [2m:[0;39m [THYMELEAF][http-nio-8080-exec-2] Exception processing template "dashboard_init": Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers
[2m2018-03-26 10:07:20.422[0;39m [31mERROR[0;39m [35m13184[0;39m [2m---[0;39m [2m[nio-8080-exec-2][0;39m [36mo.a.c.c.C.[.[.[/].[dispatcherServlet] [0;39m [2m:[0;39m Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause
org.thymeleaf.exceptions.TemplateInputException: Error resolving template "dashboard_init", template might not exist or might not be accessible by any of the configured Template Resolvers
at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:246)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
something wrong overall..
in Controller replace top /dashboard with /
#Controller
#RequestMapping("/")
public class DashboardController {
#RequestMapping("/")
String init(){
System.out.println("Dashboard - init()");
return "dashboard_init";
}
}
Also as fas as I remember return "dashboard_init" is expecting dashboard_init.html template to be returned
Probably you want redirect or something to /dashboard_init, do like
#RequestMapping(value = "/", method = RequestMethod.GET)
public void index(final HttpServletResponse response) {
response.setStatus(HttpStatus.OK.value());
response.sendRedirect( "/wherever-you-want");
}
This series of tutorials will help you to integrate Spring Security with Angular, using different approaches. Starting with Basic Auth
Spring Security and Angular
Related
I am building a reddit clone with Spring Boot and AngularJS. Currently I have a rest repository of posts and comments that can be accessed when a user logs in.
Security Config file
package com.example.MundaneHeroes.Configuration;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception
{
http.antMatcher("/**").authorizeRequests().antMatchers("/", "/login**", "/account/**").permitAll().anyRequest().authenticated().and().oauth2Login();
}
}
application.yml file
server:
port: 8082
servlet:
session:
cookie:
name: UISESSION
spring:
h2:
console:
enabled: true
settings:
web-allow-others: true
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
password:
username: sa
security:
oauth2:
client:
registration:
custom-client:
client-id: R2dpxQ3vPrtfgF72
client-secret: fDw7Mpkk5czHNuSRtmhGmAGL42CaxQB9
client-name: Auth Server
scope: user_info
provider: custom-provider
redirect-uri-template: http://localhost:8082/login/oauth2/code/
client-authentication-method: basic
authorization-grant-type: authorization_code
provider:
custom-provider:
token-uri: http://localhost:8081/auth/oauth/token
authorization-uri: http://localhost:8081/auth/oauth/authorize
user-info-uri: http://localhost:8081/auth/user/me
user-name-attribute: name
I mostly followed this tutorial here on creating an authorization server
tutorial
My problem is I havent been able to add users to this authorization server
I added a User entity, and a JPA repository of users and added code to configure additional accept additional users beyond the 1 in the tutorial. I've overriden user details, so I believe thats a good start.
#Value("${user.oauth.user.username}")
private String username;
#Value("${user.oauth.user.password}")
private String password;
#Autowired
private UserService userDetailsService;
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.inMemoryAuthentication().withUser(username).password(passwordEncoder().encode(password)).roles("ADMIN");
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
//auth to userService and password encoder
}
However I have no idea how to accept data from the /account/ page where the client would create a new account.
here is the html code for account.html
<!DOCTYPE html>
<html lang="en" ng-app="userApp">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.0/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.0/angular-route.js"></script>
<script src="account.js"></script>
<script src="bower_components/angular/angular.js"></script>
<link rel="stylesheet" href="../app.css" />
</head>
<body ng-controller="UserController">
Username <input ng-model="username"/><br>
Password <input ng-model="password"/><br>
Email <input ng-model="email"/><br>
<input type="button" value="Send" ng-click="postuser(username, password, email)" />
<p>StatusCode: {{statusval}}</p>
<p>Status: {{status}}</p>
<p>Response {{headers}}</p>
</body>
</html>
and account.js
'use strict';
var userApp = angular.module('userApp', []);
userApp.controller('UserController', function UserController($scope, $http) {
$scope.username = null;
$scope.password = null;
$scope.email = null;
$scope.postuser = function(username, password, email){
var data = {
username: username,
password: password,
email: email
};
$http.post("http://localhost:8081/auth/users", JSON.stringify(data)).then (function (response){
if (response.data)
$scope.msg = "Post Data Submitted Successfully!";
}, function (response) {
$scope.msg = JSON.stringify(data)
$scope.statusval = response.status;
$scope.statustext = response.statusText;
$scope.headers = response.xhrStatus;
})
};
})
I have been trying to modify the http security expressions in the original code
#Override
protected void configure(HttpSecurity http) throws Exception
{
//http.authorizeRequests().antMatchers("/h2-console/**").permitAll();
http.requestMatchers().antMatchers("/login", "/oauth/authorize").and().authorizeRequests().anyRequest().authenticated().and().formLogin().permitAll();
// http.requestMatchers().antMatchers("/login", "/oauth/authorize").and().authorizeRequests().antMatchers("/login").authenticated().and().formLogin().permitAll().and().authorizeRequests().antMatchers("/h2-console/**").anonymous();
//http.authorizeRequests().antMatchers("/h2-console/**").anonymous();
// http.requestMatchers().antMatchers("/login", "/oauth/authorize").and().authorizeRequests().anyRequest().
// http.requestMatchers().antMatchers("/login", "/oauth/authorize");
//http.authorizeRequests().anyRequest().authenticated().and().formLogin().permitAll();
//http.antMatcher("/**").requestMatchers("/h2-console/**")
// http.requestMatchers().antMatchers("/login", "/oauth/authorize");
// http.authorizeRequests().antMatchers("/h2-console/**").permitAll();
// .requestMatchers().antMatchers("/login", "/oauth/authorize");
// http.authorizeRequests().antMatchers("/").permitAll().antMatchers("/h2-console/**").permitAll();
//to run h2 might use profiles later
http.csrf().disable();
http.headers().frameOptions().disable();
}
The things that are commented out are things that I have tried
I have a controller in the Auth server that accepts POST requests. When I comment out the normal httpSecurity expressions and add code to disable csrf protection and disable headers, I can create accounts. (obviously this isn't a good solution)
At this point I'm kinda stuck, I also suspect that this isn't at all the correct way of sending data to a secure server. But, I haven't been able to find any guides online
So, can anybody help or point me in the right direction?
I came up with this line of code to get everything working
http
.csrf().disable()
.headers().frameOptions().disable()
.and()
.requestMatchers()
.antMatchers("/login")
.and()
.authorizeRequests()
.antMatchers("/login").authenticated()
.and().formLogin().permitAll()
.and()
.requestMatchers()
.antMatchers("/oauth/authorize")
.and()
.authorizeRequests()
.antMatchers("/oauth/authorize").authenticated()
.and().formLogin().permitAll()
.and().requestMatchers()
.antMatchers("/h2-console/**")
.and().authorizeRequests()
.antMatchers("/h2-console/**").permitAll()
.and().requestMatchers()
.antMatchers("/users")
.and()
.authorizeRequests().antMatchers("/users").permitAll()
.and()
.requestMatchers().antMatchers("/user/currentUser")
.and()
.authorizeRequests().antMatchers("/user/currentUser").permitAll();
I'm trying to upload .pdf file with jQuery AJAX to Spring MVC 5 with Spring Security 5 back-end running on Tomcat and faced multiple issues depending on Spring configuration
NOTE:
File upload should be available without authentication
Front-end
Markup:
<div id="upload-modal" class="modal">
<div class="modal-content">
<h4>Upload</h4>
<form action="#" enctype="multipart/form-data">
<div class="file-field input-field">
<div class="btn">
<span>View...</span>
<input type="file" name="file" accept="application/pdf">
</div>
<div class="file-path-wrapper">
<label>
<input class="file-path validate" type="text">
</label>
</div>
</div>
</form>
</div>
<div class="modal-footer">
Cancel
Upload
</div>
</div>
csrf header for all the requests:
$(document).ready(function () {
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function (e, xhr, options) {
xhr.setRequestHeader(header, token);
});
});
Uploading with jQuery AJAX:
$("#upload-bttn").click(function () {
var $uploadModal = $("#upload-modal");
const fileName = $uploadModal.find(".file-path").val();
const extension = fileName.substr(fileName.lastIndexOf(".") + 1);
if (extension === "pdf") {
$.ajax({
url: "/upload",
type: "POST",
data: new FormData($uploadModal.find("form").get(0)),
processData: false,
contentType: false,
success: function () {
console.log("success")
},
error: function () {
console.log("error")
}
});
} else {
M.toast({html: 'Selected file is not .pdf'});
}
});
Back-end
General configuration looks like below. It is modified depending on the cases
Security Initialization:
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityContext.class);
}
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
}
Application initialization:
public class ApplicationInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
servletContext.getSessionCookieConfig().setHttpOnly(true);
servletContext.getSessionCookieConfig().setSecure(true);
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(WebAppContext.class);
ServletRegistration.Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
Case 1 - CommonsMultipartResolver bean definition
CommonsMultipartResolver bean definition:
#Bean
public CommonsMultipartResolver multipartResolver(
#Value("${max.upload.size}") Integer maxNumber,
#Value("${max.size}") Integer maxSize) {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1024 * maxSize * maxNumber);
resolver.setMaxUploadSizePerFile(maxSize);
resolver.setMaxInMemorySize(maxSize);
resolver.setDefaultEncoding("UTF-8");
try {
resolver.setUploadTempDir(new FileSystemResource(System.getProperty("java.io.tmpdir")));
} catch (IOException e) {
e.printStackTrace();
}
return resolver;
}
I remember there was strange Spring behavior when MultipartResolver bean should be named "multipartResolver" explicitly. I tried both #Bean and #Bean("multipartResolver") with configuration above and had same result (despite bean above is named "multipartResolver" as per method name)
Result:
Error 500 - Unable to process parts as no multi-part configuration has been provided
Case 2 - MultipartConfigElement in Servlet registry
removed CommonsMultipartResolver bean
added StandardServletMultipartResolver bean
added MultipartConfigElement to ApplicationInitializer
StandardServletMultipartResolver bean definition:
#Bean
public StandardServletMultipartResolver multipartResolver() {
return new StandardServletMultipartResolver();
}
Updated ApplicationInitializer:
#Override
public void onStartup(ServletContext servletContext) {
...
servlet.setMultipartConfig(new MultipartConfigElement(
System.getProperty("java.io.tmpdir")
));
}
As per Spring documentation:
Ensure that the MultipartFilter is specified before the Spring Security filter. Specifying the MultipartFilter after the Spring Security filter means that there is no authorization for invoking the MultipartFilter which means anyone can place temporary files on your server. However, only authorized users will be able to submit a File that is processed by your application
As I need to allow not authenticated users to upload the files I tried both before and after in SecurityInitializer as below with the same result
#Override
protected void beforeSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
or
#Override
protected void afterSpringSecurityFilterChain(ServletContext servletContext) {
insertFilters(servletContext, new MultipartFilter());
}
Result:
Error 403
Questions
What do I miss in the configuration?
Thoughts
CommonsMultipartResolver would be preferable as allows to drive it with Spring properties
Something wrong with Spring Security context setup
There is allowCasualMultipartParsing="true" option (did not test) which I wouldn't like to stick to as its Tomcat specific
Updates
With disabled Spring Security everything works properly
http.authorizeRequests().antMatchers("/**").permitAll(); remains as the only security context configuration so don't think its Security context configuration issue
Set multipart resolver bean name explicitly in MultipartFilter in
beforeSpringSecurityFilterChain(ServletContext servletContext) and still no luck
Adding of _csrf token to the request header did not work for both cases
Realized that I miss additional WebAppContext class in SecurityInitializer constructor. Now error 500 disappeared but 403 appeared for case 1. Logging says that I have invalid csrf token despite I added it to the header like above
Tried to submit the form with csrf token including hidden input <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> yet the result is the same - error 403 with invalid token statement
After two days of struggling:
Constructor should contain both security and application context configuration classes
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(SecurityContext.class, WebAppContext.class);
}
}
Application context (WebAppContext) should contain MultipartResolver bean definition
#Bean
public CommonsMultipartResolver multipartResolver(
#Value("${max.upload.size}") Integer maxNumber,
#Value("${max.size}") Integer maxSize) {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1024 * maxSize * maxNumber);
resolver.setMaxUploadSizePerFile(maxSize);
resolver.setMaxInMemorySize(maxSize);
resolver.setDefaultEncoding("UTF-8");
try {
resolver.setUploadTempDir(new FileSystemResource(System.getProperty("java.io.tmpdir")));
} catch (IOException e) {
e.printStackTrace();
}
return resolver;
}
In my case after application initialization csrf token inside Spring CsrfTokenRepository was empty for some reason so when Spring been comparing token from client request header with null in CsrfFilter Spring was returning error 403. I configured csrf in security context in the following way:
#Override
protected void configure(HttpSecurity http) throws Exception {
...
http.csrf().csrfTokenRepository(new CookieCsrfTokenRepository());
...
}
Now csrf token is passed in cookies with first server response to the browser and the repository generates and caches a token to compare against the one coming from the client so comparison passes successfully
Here CookieCsrfTokenRepository may also be declared as CookieCsrfTokenRepository.withHttpOnlyFalse() if you would like to grab the token from cookie and set it into csrf header, but I have chosen to go with meta tags approach above
I know that there are many topics with this problem because I've gone over all of them. But still I haven't found any solution.
Basically I have a ResourceHandler that maps a resource but doesn't find the css file while entering the jsp. I'm using Spring 4.3.9.RELEASE.
The project look like this
Web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
com.res.context.MvcConfig
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
Configuration file looks like this:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = {"com.res"})
public class MvcConfig extends WebMvcConfigurerAdapter {
//I've already tried without this
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
super.configurePathMatch(configurer);
configurer.setUseRegisteredSuffixPatternMatch(false);
configurer.setUseSuffixPatternMatch(false);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// TODO Auto-generated method stub
configurer.enable();
}
#Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
}
The jsp file
I've already tried many values of href
//Those two give 404 error code
<link rel="stylesheet" href="resources/style.css" type="text/css">
<link rel="stylesheet" href="/resources/style.css" type="text/css">
//Those two give jsp fatal error with NullPointerException
<link rel="stylesheet" href='<c:uri>value="/resources/style.css"</c:uri> 'type="text/css">
<link rel="stylesheet" href='<c:uri>value="resources/style.css"</c:uri> 'type="text/css">
Controller
#Controller
#RequestMapping("/")
public class MyController {
private static final Logger logger = Logger.getLogger(MyController.class);
#RequestMapping("/site")
public ModelAndView site()
{
ModelAndView model = new ModelAndView("site");
model.addObject("txt", "Model");
return model;
}}
And finally most important part
logs
2017-07-18 20:56:35 DEBUG DispatcherServlet:869 - DispatcherServlet with name 'mvc-dispatcher' processing GET request for [/res/site.htm]
2017-07-18 20:56:35 DEBUG RequestMappingHandlerMapping:310 - Looking up handler method for path /site.htm
2017-07-18 20:56:35 DEBUG RequestMappingHandlerMapping:320 - Did not find handler method for [/site.htm]
2017-07-18 20:56:35 DEBUG RequestMappingHandlerMapping:310 - Looking up handler method for path /site.htm
2017-07-18 20:56:35 DEBUG RequestMappingHandlerMapping:317 - Returning handler method [public org.springframework.web.servlet.ModelAndView com.res.controller.MyController.site()]
2017-07-18 20:56:35 DEBUG DefaultListableBeanFactory:251 - Returning cached instance of singleton bean 'myController'
2017-07-18 20:56:35 DEBUG DispatcherServlet:955 - Last-Modified value for [/res/site.htm] is: -1
2017-07-18 20:56:35 DEBUG DefaultListableBeanFactory:1670 - Invoking afterPropertiesSet() on bean with name 'site'
2017-07-18 20:56:35 DEBUG DispatcherServlet:1280 - Rendering view [org.springframework.web.servlet.view.JstlView: name 'site'; URL [/WEB-INF/views/site.jsp]] in DispatcherServlet with name 'mvc-dispatcher'
2017-07-18 20:56:35 DEBUG JstlView:432 - Added model object 'txt' of type [java.lang.String] to request in view with name 'site'
2017-07-18 20:56:35 DEBUG JstlView:166 - Forwarding to resource [/WEB-INF/views/site.jsp] in InternalResourceView 'site'
2017-07-18 20:56:36 DEBUG DispatcherServlet:1000 - Successfully completed request
2017-07-18 20:56:36 DEBUG DispatcherServlet:869 - DispatcherServlet with name 'mvc-dispatcher' processing GET request for [/res/resources/style.css]
2017-07-18 20:56:36 DEBUG RequestMappingHandlerMapping:310 - Looking up handler method for path /resources/style.css
2017-07-18 20:56:36 DEBUG RequestMappingHandlerMapping:320 - Did not find handler method for [/resources/style.css]
2017-07-18 20:56:36 DEBUG RequestMappingHandlerMapping:310 - Looking up handler method for path /resources/style.css
2017-07-18 20:56:36 DEBUG RequestMappingHandlerMapping:320 - Did not find handler method for [/resources/style.css]
2017-07-18 20:56:36 DEBUG SimpleUrlHandlerMapping:192 - Matching patterns for request [/resources/style.css] are [/resources/**]
2017-07-18 20:56:36 DEBUG SimpleUrlHandlerMapping:226 - URI Template variables for request [/resources/style.css] are {}
2017-07-18 20:56:36 DEBUG SimpleUrlHandlerMapping:140 - Mapping [/resources/style.css] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[ServletContext resource [/resources/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver#4c531172]]] and 1 interceptor
2017-07-18 20:56:36 DEBUG DispatcherServlet:955 - Last-Modified value for [/res/resources/style.css] is: -1
2017-07-18 20:56:36 DEBUG DispatcherServlet:1048 - Null ModelAndView returned to DispatcherServlet with name 'mvc-dispatcher': assuming HandlerAdapter completed request handling
2017-07-18 20:56:36 DEBUG DispatcherServlet:1000 - Successfully completed request
I hope that someone knows what is wrong here because after few hours I can't think of anything more.
The problem is because you have the folder resources out of webapp and your servlet is looking at webapp as /.
First way:
Move your resources folder under webapp and then into your applicationContext.xml add the next line:
<mvc:resources mapping="/css/**" location="/resources/" />
then you can import your style.css file like this:
<link href="<c:url value="/resources/style.css" />" rel="stylesheet">
Second way:
you can create another Servlet for static content:
First add the next few lines in your web.xml
<servlet>
<servlet-name>resources</servlet-name>
<servlet-class>com.res.servlets.DefaultServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>resources</servlet-name>
<url-pattern>/resources/*</url-pattern>
</servlet-mapping>
next step, create the package com.res.servlet the under that package create the class DefaultServlet like this:
public class DefaultServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
// Tomcat, Jetty, JBoss, and GlassFish
private static final String COMMON_DEFAULT_SERVLET_NAME = "default";
// Resin
private static final String RESIN_DEFAULT_SERVLET_NAME = "resin-file";
// WebLogic
private static final String WEBLOGIC_DEFAULT_SERVLET_NAME = "FileServlet";
// WebSphere
private static final String WEBSPHERE_DEFAULT_SERVLET_NAME = "SimpleFileServlet";
public String scanDefaultServlet(){
if(this.getServletContext().getNamedDispatcher(COMMON_DEFAULT_SERVLET_NAME) != null) {
return COMMON_DEFAULT_SERVLET_NAME;
} else if(this.getServletContext().getNamedDispatcher(RESIN_DEFAULT_SERVLET_NAME) != null) {
return RESIN_DEFAULT_SERVLET_NAME;
} else if(this.getServletContext().getNamedDispatcher(WEBLOGIC_DEFAULT_SERVLET_NAME) != null){
return WEBLOGIC_DEFAULT_SERVLET_NAME;
} else if(this.getServletContext().getNamedDispatcher(WEBSPHERE_DEFAULT_SERVLET_NAME) != null){
return WEBSPHERE_DEFAULT_SERVLET_NAME;
} else {
throw new IllegalStateException("Cannot determine what Server you currently use");
}
}
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
RequestDispatcher rd = getServletContext().getNamedDispatcher(this.scanDefaultServlet());
HttpServletRequest wrapped = new HttpServletRequestWrapper(req) {
public String getServletPath() {return "";}
};
rd.forward(wrapped, resp);
}
}
now you can call your resources like this:
<link rel="stylesheet" type="text/css" href="<c:url value="/resources/resources/style.css"/>">
Make sure you has imported the tag libs like this at the top of your view:
<%#taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
Regards,
I am setup a sample Code base using Spring MVC in Eclipse and JBoss 6.2.
But I get '404' with http://localhost:8080/rest/simple/main
Jboss log as below:
2015-07-29 11:51:27,356 ERROR [controller.simpleController] (http-/0.0.0.0:8080-1) get request
2015-07-29 11:51:27,391 WARN [org.springframework.web.servlet.PageNotFound] (http-/0.0.0.0:8080-1) No mapping found for HTTP request with URI [/rest/WEB-INF/views/main.jsp] in DispatcherServlet with name 'dispatcher'
Directory :
>rest-server-simple
> -src
> -main
-java
-config
-InitConfig.java
-ServletConfig.java
-controller
-simpleController.java
> -webapp
> -WEB-INF
-jboss-web.xml
>views
-main.jsp
InitConfig:
public class InitConfig implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(ServletConfig.class);
ServletRegistration.Dynamic registration = servletContext.addServlet("dispatcher",new DispatcherServlet(ctx));
registration.setLoadOnStartup(1);
registration.addMapping("/*"); }}
ServletConfig:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages ="controller")
public class ServletConfig {
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}
jboss-web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web>
<context-root>/rest</context-root>
</jboss-web>
simpleController:
#Controller
#RequestMapping(value = "/simple")
public class simpleController {
private static final Logger logger = LoggerFactory.getLogger(simpleController.class);
#RequestMapping(value = "/main", method = RequestMethod.GET)
public String hello(){
logger.error("get request");
return "main";
}
}
registration.addMapping("/*");
Change it to
registration.addMapping("/");
There is a difference between /* and / .
/* indicates that every request will be handled by DispatcherServlet, in this case retrieval of a jsp or anything like .../abc.xyz etc will also be forwarded to Dispatcher, so when controller requests for a view it actually looks for RequestMapping mapped for /WEB-INF/views/main.jsp but
/ tells container that only those requests that do not have pathinfo i.e /rest/simple/main will be handled by DispatcherServlet.
UPDATE#1
Hmm.. What I have found that jboss AS 7 doesn't like overriding default servlet i.e. / without web.xml and hence you are still getting 404 and not even getting anything on the logger, Reason being simple is that Dispatcher is never mapped to any url. If you want to check that just add following after addMapping("/*");
System.out.println("registration.getMappings() = " + registration.getMappings());
It works fine with Tomcat >= 7.0.15 or WildFly have checked on both.
To make it work on JBoss7 there are few options:
1. Change DispatcherServlet mapping from / to *.htm or something except DefaultServlet Mapping.
2. Switch your Configuration to web.xml. You will have to initialize DispatcherServlet there and pass Annotated class as `contextConfigLocation. Check here for REF
I am creating a spring security application using Spring 4.0.2.RELEASE and Spring Security 3.2.3.RELEASE using entirely java configuration, no xml. The configuration for security seems to be working correctly and is generating the login page correctly and authenticating. However I get 404 errors for all of my pages.
I have controllers and jsp pages set up for each page. When I run the application, I see log messages showing that the controllers were mapped
Mapped "{[/ || /welcome] ... onto ... WelcomeController.welcome()
However, when I try to hit one of those URLs, I get the login page, then on sucessfull login get a 404 and I see nothing in the log.
Below you will find my controller, my 2 configuration classes, and my 2 initializes.
WelcomeController.java
#Controller
public class WelcomeController {
#RequestMapping(value = {"/", "/welcome"})
public ModelAndView welcome() {
System.out.println("welcome invoked");
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("welcome");
return modelAndView;
}
}
Below You will find my configuration files
WebConfig.java
#EnableWebMvc
#Configuration
#ComponentScan({ "com.myproject.pagegen.controller" })
public class WebConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver
= new InternalResourceViewResolver();
resolver .setViewClass(JstlView.class);
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
SecurityConfig.java
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/").hasRole("USER")
.antMatchers("/welcome").hasRole("USER")
.anyRequest().anonymous()
.and().formLogin();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
WebAppInitializer.java
public class WebAppInitializer
extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] { WebConfig.class, SecurityConfig.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
SecurityWebAppInitializer.java
public class SecurityWebAppInitializer
extends AbstractSecurityWebApplicationInitializer { }
UPDATE:
I did find something interesting. If I changed my servletMapping to /* instead of /, I would then get a log message showing that the controller was invoked, but it would have no mapping for the jsp. It seems like it is trying to map the jsp url to the controllers.
welcome invoked
org.springframework.web.servlet.PageNotFound noHandlerFound
WARNING: No mapping found for HTTP request with URI [/ROOT/WEB-INF/jsp/welcome.jsp] in DispatcherServlet with name 'dispatcher'
I had a similar issue when running a project using Eclipse and Tomcat. Try running the application using Spring Tool Suite and use the VMware vFabric tc server.
I also got it to work in Eclipse and Tomcat by manually updating the version of Tomcat installed. Try the latest version 7.0.54 https://tomcat.apache.org/download-70.cgi
It seems to me that all troubles in your authentication mechanism and not in controller mapping for URL-s.
It is a good tutorial about Security Java config LINK
When you try go to your pages without authentifications you are redirected to 'login page'. If I've understand right - your authentification process is fail all the time. In your configuration you can't go to '/welcome' and '/' pages without being authorized.
Try to add and change next things:
protected void configure(HttpSecurity http) throws Exception {
// Here you can define your custom redirections
.loginPage("[some url]")
.failureUrl("[some url]")
.loginProcessingUrl("[some url]")
.defaultSuccessUrl("[some url]")
// Here the credentials, sended to the authentification mechanizm (If you use clasic 'UsernamePasswordAuthenticationFilter')
.usernameParameter("j_username")
.passwordParameter("j_password")
.permitAll()
// Permitions for access pages
http.authorizeRequests()
.antMatchers("/welcome", "/").permitAll()
// Access restriction without using roles
.antMatchers([some other URL-s]]).authenticated()
// or using ROLES
.antMatchers([some other URL-s]]).hasRole("USER")
// Here you can define your custom redirections for logout
.and()
.logout()
.logoutUrl("/logOut")
.logoutSuccessUrl("/welcome");
}
If you use clasic 'UsernamePasswordAuthenticationFilter' you must define it as bean (If you use another fllter, of course you must define it):
#Bean
public UsernamePasswordAuthenticationFilter filter() {
UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();
filter.setAuthenticationManager(providerManager());
return filter;
}
And then in JSP page use form like this ('j_username' and 'j_password' parameters required in default authentication with 'UsernamePasswordAuthenticationFilter')
<form name="signIn" action="[your SignIn URL]" method="POST">
<input type="text" name="j_username"></label>
<input type="password" name="j_password"></label>
<input type="submit" value="Log In">
</form>
You must try the remote debugging to find what fail in your authentication process.