I am using spring boot and angular in front end. Sometimes I want to redirect directly from a service. We are using only .html, .css and script files (no .jsp).
This is return "static/pages/savepassword.html"; how I tried to redirect to savepassword
So 1st question how do I redirect properly in this case?
Next, Spring doesn't find my html files. I have this structure
I read that spring should find all static files in "static" automatically, but it doesn't. So I tried to configure my ViewControllerRegistry and RessourceControllerRegistry
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
// registry.addResourceHandler("/stylesheets/**").addResourceLocations("/stylesheets/");
// registry.addResourceHandler("/scripts/**").addResourceLocations("/scripts/");
// registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/savePassword").setViewName("static/pages/savePassword.html");
registry.addViewController("/login").setViewName("static/pages/login.html");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
When I call "static/pages/login.html" directly, it works. When i want to call "/login" it says 404.
2nd question: What do I need to configure and what not?
UPDATE-1: Spring Security
http
.authorizeRequests().antMatchers("/login", "/logout", "/user/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.accessDeniedHandler(new CustomAccessDeniedHandler())
.authenticationEntryPoint(new CustomAuthenticationEntryPoint())
.and()
.requiresChannel()
.anyRequest().requiresSecure()
.and()
.csrf()
.disable()
//.csrfTokenRepository(csrfTokenRepository())
.addFilterBefore(new MDCFilter(), ExceptionTranslationFilter.class)
// .addFilterBefore(new CorsFilter(),// ChannelProcessingFilter.class)
//.addFilterAfter(new CsrfHeaderFilter(), CsrfFilter.class)
.formLogin()
.successHandler(new CustomSuccessAuthenticationHandler())
.failureHandler(new CustomFailureHandler())
.loginPage("/login").permitAll();
UPDATE-2
I updated my structure, but still have problems with redirecting from /login to the custom login.
UPDATE-3
I had a look at the 2nd comment by Bob Shields: https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot
I did the following:
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
super.addResourceHandlers(registry);
registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/savepassword").setViewName("savepassword.html");
registry.addViewController("/login").setViewName("login.html");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
I set .html because otherwise /login would cause recursive problems. With this config, I can enter /login and /templates/login.html will be called (same for savepassword.html).
Now, I would like to know how to include my css/js in html:
<link rel="stylesheet" type="text/css" href="..resources/stylesheets/bootstrap.min.css" />
<script src="..resources/scripts/angular.min.js"></script>
<script src="..resources/scripts/login.js"></script>
This doesn't work...but I cann call my scripts directly with /scripts/login.js.
In my opinion it is better to put your *.HTML files in your resources folder with all the static CSS FONTS and so on like in the picture below. In webapp you can have a WEB-INF folder only.
I am using this kind of structure and I have no problems finding any classes css or htmls and I do not use extensions at the end in java ("static/pages/login.html" like yours). If I want to access html I go like "user/someHtml" if it is in folder user for ex.
I know this answer is not precisely the one you are looking for but it may help you in someway.
EDIT: Now for the :
#Override
public void addViewControllers(ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/savePassword").setViewName("static/pages/savePassword.html");
registry.addViewController("/login").setViewName("static/pages/login.html");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
Lets say you have a Controller:
#Controller
#RequestMapping("/myController")
public class MyController{
...
#RequestMapping(value = "/login")
public String login(Model model) {
// Your code ....
return "login"; //<- this is your login.html if it is directly in resource/templates folder
}
#RequestMapping(value = "/savePassword")
public String savePassword(Model model) {
// Your code ....
return "savePassword"; //<- this is your savePassword.html if it is directly in resource/templates folder
}
.....
}
Now your addViewController show look like this :
#Override
public void addViewControllers(ViewControllerRegistry registry) {
super.addViewControllers(registry);
registry.addViewController("/savePassword").setViewName("forward:/myController/savePassword");
registry.addViewController("/login").setViewName("forward:/myController/login");
registry.setOrder(Ordered.HIGHEST_PRECEDENCE);
}
You see in your addViewController you are trying to access a CONTROLLER not a HTML you will access your HTML through the CONTROLLER you access from the method addViewControllers.
UPDATE:
If you put your CSS into your static folder in resources like :
You can simply access them like this :
<link rel="stylesheet" th:href="#{/css/bootstrap-datetimepicker.min.css}" />
Hope this helps!
Ok, now I totally got it. I try to be specific as possible.
My final structure
1. Static Ressources
Spring has some default configuration where you can put your static ressources without configure it. You need to create those folders in src/main/resources
/META-INF/resources/
resources (yes, an additional resources folder in src/main/resources
static
public
Have a look here in Spring Doku. For an example, check the 2nd comment by Bob Shields:
After creating a bunch of "findme.txt" files, each of which had different "source-relative" path string inside, I found that the following locations can be fetched with "http://host/findme.txt" are:
src/main/resources/public/findme.txt
src/main/resources/static/findme.txt
src/main/resources/resources/findme.txt - (yes, resources^2!)
src/main/resources/findme.txt - not found!!
I placed my folder "scripts" and "stylesheets" in the static folder. When I want to include files from these folders in html, the source looks like src="scripts/file.js"because the files and folders in staticare found directly rooted to host/scripts/file.js
No ResourceHandlerRegistry was necessary for me!
2. View Controller
I wanted to root my login.html to /login, savepassword.html to /savepassword and my index.html to /. Here is what I did:
registry.addViewController("/savepassword").setViewName("savepassword.html");
registry.addViewController("/login").setViewName("login.html");
registry.addViewController("/").setViewName("loggedin/index.html");
I set *.html because otherwise I had a problem that spring didn't know to differ between /login and the login(.html).
As you can see I placed the index.html in a different folder called "loggedin". This folder is right in my public-folder (found by Spring, see 1.). Why do I do this? Have a look at 3.
3. Spring Security
.authorizeRequests().antMatchers("/stylesheets/**", "/scripts/**","/login", "/user/**").permitAll()
...omitted further configuration...
.loginPage("/login").permitAll();
First, I want that a user always has to be logged in before he can visit any other sites. So all ressources for loginpage must be available.
This piece of code makes every file in stylesheets and scripts accessible. Remember stylesheets and scripts-folder are placed in static. I had to set "/login" because of the custom loginpage.
Honestly I thought that the .loginPage("/login").permitAll();would grant access, but it doesn't. Without it Spring tries to access the "/login", but can't cause of no rights and then automatically redirects to login => ERR_TOO_MANDY_REDIRECTS.
"/user/**" is just for some of my services that every user can use.
As you can see, I don't give access to "loggedin"-folder, so that every visitor can enter files in "loggedin" only if he is loggedin ;)
4. Redirecting
I just marked my whole controller with #RequestController, which implements #ResponseBody. When I return a String like return "redirect:/savepassword";, Spring will set this as ResponseBody and it will be interpreted as String/Json(?).
I had to mark the controller as #Controller and set #ResponseBody to every service that must return json or sth. My service that shall redirect was only marked with #RequestMapping and method=RequestMethod.GET. Finally, I return a String like return "redirect:/savepassword"; This will make Spring redirect to /savepassword, which actually is placed in src/main/resources/public/savepassword.html.
Finally, it works and it was hard enough to find how spring serves the files. I hope it helps someoneelse :)
Related
I have strange question to you, when I built a simple spring boot application I created for testing that everything working as it should a simple Controller called HomeController.java, but when I go to address in my case 'localhost:5000/app' I thought that there should appear Hello World! but it redirect me to address 'localhost:5000/login' and a login form box appear. Can someone explain me what happend?
#Controller
public class HomeController {
#RequestMapping(value = "/app")
public String index() {
return "Hello World!";
}
}
Just create a class for security configuration like this
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/app/**",).permitAll();
}
That's because you are not allowing anonymous access to your /app url, which in return redirects you to the login form as to authenticate the user. You would need to add the following configuration to your spring security.
.authorizeRequests()
.antMatchers("/app/**")
.permitAll()
The ** will allow any matches with the entire directory tree, including subdirectories.
I've been struggling for a few days over this. Aside from quirky things that seem to be happening inconsistently and unpredictably by simply commenting out a bit of code, running the program, and then uncommenting and running again, I'm failing to understand how overriding various configure methods are working.
I want WebSecurity to always ignore "/static/**".
Upon launching the application and navigating to the homepage, I can access all of the pages for which I have permitted all, but all of the content in "/static/**" is being ignored until after I have navigated to the login page and logged in as an authenticated user. So the application just appears as white pages with text, without any of the styling at all until logged in.
Here is the code for my AppSecurityConfig class. I have omitted the helper methods for handling success and failure of logging in, and I also have to different account types that serve different roles, so I have only included one account here for simplification. The part where I believe the problem exists is in the configure(WebSecurity web) method where I am calling the .ignoring() method and passing the "/static/**" arg. Thank you in advance:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CompanyService companyService;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(companyService);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/static/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/",
"/account_registration",
"/candidate_registration",
"/addCandidate",
"/company_registration",
"/addCompany",
"/select_account_type",
"/candidate_login",
"/company_login").permitAll()
.antMatchers("/company_profile").hasRole("COMPANY")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/company_login")
.permitAll()
.successHandler(companyLoginSuccessHandler())
.failureHandler(companyLoginFailureHandler())
.and()
.logout()
.logoutSuccessUrl("/");
}
}
The path to my static folder is "src/main/resources/static", but I did what Sam said and opened the developer tools and realized that all of the contents within the "static" directory were being referenced directly. For example, there are directories referenced in this way: "/vendor/..." and "/images/...", that were being referenced but ignored due to security. There are also some files in the "static" directory like "app.css", "app.js" and "favicon.png" that are having some strange behavior. It appears that they are not being ignored but different colors and styling are being displayed unless I also add them as arguments to the .gitIgnoring() method like "/app.css" etc. This project was built by working through a TeamTreehouse tutorial and then refactoring and adding custom styling between a few people on my 6 person team, and I'm pretty sure there are multiple things under the hood inherited in this project that myself and the front end people are not understanding when it comes to the styling.
The fix that seems to work, although maybe not ideal, was removing "/static/**" from the .ignoring() method and replacing it with all of the contents that were actually inside the "static/" directory:
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers( "/images/**",
"/vendor/**",
"/app.css",
"/app.js",
"/favicon.png");
}
I have a Spring Boot application with the following structure:
src
|--main
---|--java
---|--resources
------|--templates
------|--application.properties
---|--webapp
------|--resources
---------|--static
------------|--css
------------|--img
---------------|--logo.jpg
------------|--js
------------|--favicon.ico
------|--WEB-INF
---------|--view
------------|--index.jsp
In my MvcConfig, I have added ResourceHandler as below
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/resources/");
}
In my JSPs, I can access the images from img folder using
<img src="<c:url value="/resources/static/img/logo.jpg" />">
Trying to access the same image in a class,
I tried the following code:
ClassPathResource cpr = new ClassPathResource("logo.jpg");
and also tried using many variations for the image path.
However, every time I receive a false value for cpr.exists()
Surprisingly, I received true when I changed the parameter to favicon.ico.
What am I doing wrong? Please explain this behaviour and also explain what is the right way to access the resource.
I would like to build a web application based on this example.
What I understood from this example is that you build 2 applications:
A UI application containing the web pages where you can login to get a token and a resource application where you ask for data using your token.
My question is where is the login check done?
The application.yml file defines the password "password" for the user "user"
security:
user:
password: password
But this file is never mentioned neither in UIApplication.java file or in any other file:
#SpringBootApplication
#RestController
public class UiApplication {
public static void main(String[] args) {
SpringApplication.run(UiApplication.class, args);
}
#Configuration
#Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.httpBasic().and()
.logout().and()
.authorizeRequests()
.antMatchers("/index.html", "/pages/home.html", "/pages/login.html", "/").permitAll()
.anyRequest().authenticated().and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
// #formatter:on
}
}
#RequestMapping("/user")
public Principal user(Principal user) {
return user;
}
#RequestMapping("/token")
public Map<String,String> token(HttpSession session) {
return Collections.singletonMap("token", session.getId());
}
}
However it works: when I login using "user" and "password" login succeeds, when I use different credentials, it fails.
Is it some high level Spring layer dealing with that? Thanks for help.
This is a little bit of a confusing example that you have picked because it uses Angular for login. So it is sending off the request using AngularJS.
To break it down, first let's have a look at the login.html page:
<form role="form" ng-submit="controller.login()">
So when we submit, we require the controller.login() method call, this is defined in the hello.js file.
}).when('/login', {
templateUrl : 'login.html',
controller : 'navigation',
controllerAs : 'controller'
Here we delegate requests sent to /login and map that to our navigation controller. If you go ahead in the hello.js script you'll see the following:
.controller('navigation',
...
var authenticate = function(credentials, callback) {
var headers = credentials ? {
authorization : "Basic "
+ btoa(credentials.username + ":"
+ credentials.password)
} : {};
$http.get('user', {
...
Here, we use make a GET request to /user. And if you take a look at the controller, you'll see that there is a User Principle being returned which is then checked against credentials.
The controller requires a User principle to be passed in for verification which is being built up in the headers part of the $http request.
I would recommend using this example if you are new to Spring Security and want to start off with something more Java oriented and simple.
Edit
It should also be noted that the user object is being created by the Spring application.yml as you said it is formed of the following:
security:
user:
password: password
This is equivalent to:
security.user.password="password"
Reference to the application.yml appendix can be found here.
So in essence, the yml file is creating a User with the given password which can be retrieved by the Principle. Take a look at the huge guide written by Spring that explains the project built in that repo you are looking at, step by step.
I have been told in this post that it is possible to have .jsp extension in URL structure using Spring. I have been trying to achieve it with the following code:
HelloController.java
#Controller
#RequestMapping("/welcome")
public class HelloController {
#RequestMapping(value="/item.jsp", method = RequestMethod.GET)
public String helloDotJSP(ModelMap model) {
System.out.println("/item.jsp RequestMapping");
model.addAttribute("message", "Spring 3 MVC Hello World");
return "item";
}
}
And when I try to access the page localhost:8080/app/welcome/item.jsp I get the error that The requested resource is not available. But when I try to just modify the extension from /item.jsp to for example /item.other it starts working. How can I add support for .jsp extension?
Thank you for your help.
You are getting the "requested resource is not available" because I your servlet mapping in the web.xml is not mapping item.jsp to the Spring DispatcherServlet.
Regardless, if your requirement is just to support old URLs, there are more effective ways of doing it.
Most lo-fi is to create jsps that with the same name and structure as the old urls, and in each of these jsps add scriptlets like
<%
response.setStatus(301);
response.setHeader( "Location", "/new-url.htm" );
response.setHeader( "Connection", "close" );
%>
I don't usually recommend scriptlets but in cases like these, its the simplest solution.
You can also add 301 redirect rules if you have a fronting webserver like apache.
Hope this helps.