I'm using Vaadin (7.6.1) with SpringBoot (1.3.0) and I got a problem regarding the application URL in the browser.
If I call "http://localhost:8080/myApp/" everything works fine -Spring Security blocks the request, redirects to login page, and after an authentication it calls the defaultSuccessUrl ("/#!"). But if the user calls "http://localhost:8080/myApp" (without the last slash) Spring Security also works but redirects to the savedRequestUrl. In this case an error message ("failed to load the bootstrap javascript: ./vaadin/vaadinbootstrap.js") is displayed.
Generally I want Spring Security to redirect to the savedRequestUrl, so what should I do? Does the URL pattern of the Servlet is beeing my problem? In this answer there is an example of an Annotation based WebServlet but if this is my problem how should I integrate it in my application?
I defined my own SpringVaadinServlet which overrides the default vaadinServlet bean. My classes look like this:
#SpringBootApplication
public class MyApp
{
public static void main(String[] args)
{
SpringApplication.run(MyApp.class, args);
}
#Bean(name = "vaadinServlet")
public CustomSpringVaadinServlet springVaadinServlet()
{
return new CustomSpringVaadinServlet();
}
}
public class ServletInitializer extends SpringBootServletInitializer
{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application)
{
[...]
return application.sources(MyApp.class);
}
}
public class CustomSpringVaadinServlet extends SpringVaadinServlet
{
private static final long serialVersionUID = -6507628193889155490L;
private final static Logger LOG = LoggerFactory.getLogger(SpringVaadinServletWsi.class);
#Override
protected void servletInitialized() throws ServletException
{
super.servletInitialized();
getService().setSystemMessagesProvider(new SystemMessagesProvider()
{
private static final long serialVersionUID = -5253418651979378723L;
#Override
public SystemMessages getSystemMessages(SystemMessagesInfo systemMessagesInfo)
{
CustomizedSystemMessages messages = new CustomizedSystemMessages();
messages.setCommunicationErrorCaption("Kommunikationsfehler");
[...]
return messages;
}
});
}
}
Related
I have set the context path for tomcat as follows:
#Component
public class CustomContainer implements
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.setContextPath("/capripol");
factory.setPort(8080);
}
}
Navigating to localhost:8080/capripol works fine and I am prompted with my login screen, however after logging in my forms and controllers do not append to the context path, so instead of navigating to /capripol/MainMenu etc. they navigate to /MainMenu. How do I set the context path such that my form actions and controllers will be appended do it - why is the tomcat factory context path not setting?
Edit: My Application class
#SpringBootApplication
public class CapripolApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(CapripolApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(CapripolApplication.class);
}
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/", "classpath:/images/")
.setCachePeriod(0);
}
}
}
A few ways to do it. You can add it to each controller, usefully if you want to change the context path
#Controller
#RequestMapping(value = "/foo")
public class bar{
#GetMapping(value = "/bar")
public void stuff(){
//doing stuff
}
}
Or you can put it in your application.properties / yml
server.servlet.contextPath=/foo/*
There are technically some other more round about ways to do it, especially if you are using an older version of Spring, but I would think the application properties is what you are looking for.
I've experience in Spring MVC, but first time using Cache. These are steps that I've done yet.
Step : 1
// In spring config
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("user");
}
// Cached Object
public class CachedUser {
private String username;
private String token;
// Public getter-setter
}
// AuthServiceImp
#Service
public class AuthServiceImp implements AuthService {
#Override
#Cacheable(value="user", key="#token")
#Transactional
public CachedUser loadUserDetailsFromDb(String username, String token) {
// codes here
}
#Override
#CacheEvict(value="user", key="#token")
#Transactional
public void removeUser(String username, String token) {
// codes here
}
}
// My Filter
public class AuthenticationTokenFilter extends UsernamePasswordAuthenticationFilter {
AuthService authService = WebApplicationContextUtils
.getRequiredWebApplicationContext(this.getServletContext())
.getBean(AuthService.class);
CachedUser user = this.authService.loadUserDetailsFromDb(username, authToken);
}
// Controller
#RestController
public class AuthenticationController {
#Autowired
private AuthService authService;
#GetMapping("logout2")
public ResponseModel logout(#RequestAttribute("username") String username,
HttpServletRequest request) {
String token = request.getHeader(tokenHeader);
authService.removeUser(username, token);
return new ResponseModel(200,"Success",null);
}
}
Whenever calling loadUserDetailsFromDb from AuthenticationTokenFilter it returns cached object (except in first call obviously). That means #Cacheable(value="user", key="#token") is working fine.
But even after I logged out and called authService.removeUser(), calling loadUserDetailsFromDb() fetches the cached object. That means #CacheEvict(value="user", key="#token") is not working.
Step: 2
Referred this and moved removeUser() to another service ( say CacheServiceImp implements CacheService ), yet same problem.
Step: 3
Reffered this and , by my understanding, moved #Cache* annotation to interface AuthService, got following error.
java.lang.IllegalArgumentException: Null key returned for cache
operation (maybe you are using named params on classes without debug
info?)
Note : Is the problem of not evicting, because I'm calling #Cacheable and #CacheEvict methods from different classes. That is from AuthenticationTokenFilter and AuthenticationController
After playing with my code, head and internet, at last, I got this solved. It's a mistake in my Spring (Security) configuration, which I failed to post with the question.
Mistake 1 :
In SecurityInitializer class
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
public SecurityInitializer() {
super(WebSecurityConfiguration.class);
}
}
As the project includes Spring MVC configuration, the constructor must not be implemented. So removed the constructor. This class, then, simply registers the springSecurityFilterChain Filter for every URL.
Mistake 2: ( THE REAL CAUSE OF ABOVE PROBLEM )
I've added my AuthenticationTokenFilter in two ways:
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
// other overrides
#Override
protected Filter[] getServletFilters() {
return new Filter[]{ new AuthenticationTokenFilter() };
}
}
and
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
// Other config
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//Other config
httpSecurity.addFilterBefore(authTokenFilter,
UsernamePasswordAuthenticationFilter.class);
}
}
This made the filter to be called twice, one inside Spring context and the other as usual Servlet filter
So removed configuration inside WebAppInitializer
Additional change
Removed #ComponentScan from WebSecurityConfiguration because it's already in SpringMvcConfig. This requires both configurations to be loaded in same context. Done by following code.
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return null;
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { SpringMvcConfig.class, WebSecurityConfiguration.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
// Removed filter registering from here (Mistake 2)
}
At last, everything working FINE :)
I want to create a custom login page for my Vaadin application.
MyUi.class
#Theme("valo")
#SpringUI
#Widgetset("pl.warta.AppWidgetSet")
public class MyUI extends UI {
#Autowired
private SpringViewProvider ViewProvider;
#Override
protected void init(VaadinRequest request) {
final Navigator navigator = new Navigator(this, this);
navigator.addProvider(ViewProvider);
setNavigator(navigator);
}
}
WebSecurityConfig.class
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll();
}
#Configuration
protected static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter {
#Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().userDnPatterns("uid={0},ou=people").groupSearchBase("ou=groups").contextSource().ldif("classpath:test-server.ldif");
}
}
}
SecuredView.class
#SpringView(name = SecuredView.NAME)
#UIScope
public class SecuredView extends VerticalLayout implements View {
private static final long serialVersionUID = 6937605817612926676L;
public static final String NAME = "";
#PostConstruct
private void postConstruct() {
setSizeFull();
setSpacing(true);
setMargin(true);
addComponent(new Label("<h3>Secured View</h3>", ContentMode.HTML));
}
#Override
public void enter(ViewChangeEvent event) {
}
}
Can you show me sample implementation of LoginView.class to match my Spring Security configure? When i created a view with NAME = "login" I'm getting:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Thu Sep 10 09:47:14 CEST 2015
There was an unexpected error (type=Not Found, status=404).
No message available
However even if page will apear, i dont know how to inject security validation on button click.
What I have didn't mentioned - login with default form is working perfectly.
I managed to create a working solution.
For everyone facing this problem i recommend to fallow this example: security-sample-managed
The only one change you need to do (ofc except throwing out model and logic) is a modify Application.java:
#Configuration
static class AuthenticationConfiguration implements AuthenticationManagerConfigurer {
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//we need to put here ldap authentication, e.g.:
auth.ldapAuthentication().userDnPatterns("CN={0},OU=Users").contextSource().url("ldap://my.ldap.server:port").managerDn("adminDn").managerPassword("adminPassword");
}
}
Everything else is configured for Vaadin already. Enjoy.
I used spring boot to do a sample service. It works fine when i run it using
"java -jar DemoLibrary.war" command in commandline. I get the proper message that "Library Application Has Started".
I did like below in Appplication.java file;
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Application.class, args);
LogService.info(Application.class.getName(), "Library Application Has Started.");
}
}
When i run it in a external tomcat, it starts fine and works also fine. But i dont see the same message as it doesnot use that main method anymore. I just see spring application started message.
Is there a way i can change that message and give as i want?
Add an ApplicationListener<ContextRefreshedEvent> typed class and register it as a #Bean.
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public ApplicationListener<ContextRefreshedEvent> startupLoggingListener() {
return new ApplicationListener<ContextRefreshedEvent>() {
public void onApplicationEvent(ContextRefreshedEvent event) {
LogService.info(Application.class.getName(), "Library Application Has Started.");
}
};
}
}
Something like this should work in both situations without duplicating code (although the code isn't complex but still).
You could use onStartup, something like:
#Override
public void onStartup(final ServletContext servletContext) throws ServletException {
LogService.info(Application.class.getName(), "Library Application Has Started.");
super.onStartup(servletContext);
}
I want to write my tag (extends TagSupport) in my spring framework. In my tag class, will use some service which should auto inject by spring. But I always get null, seems spring can't inject service instance in my tag class.
The code is like the following:
public class FetchTagNameTag extends TagSupport {
#Autowired
private TaskService taskService;
...
taskService is always null.
How can I resolve this?
Thanks.
Have a try by utilizing RequestContextAwareTag. It will offer you methods to obtain RequestContext and then WebApplicaitonContext. Have a look at here.
JSP tag objects are not managed by Spring, they are managed by the servlet container. As a result, you cannot autowire stuff into your tags.
If you need to get hold of beans from the spring appcontext, then your Spring MVC controller needs to set the bean as a request attribute (using request.setAttribute()), so that the tag object can get hold of it.
Annotate your Tag-Implementation with #Configurable and add <context:component-scan base-package="your.webapp"> to your Spring-Configuration.
Check out these spring packages in the spring reference docs and in the spring source:
org.springframework.web.servlet.tags
org.springframework.web.servlet.tags.form
If nothing else, those will show you how the spring developers wrote the spring tags.
What you could do is create a static method like this:
public static void autowireAllFor(Object target) {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(...yourBeanFactory...);
bpp.processInjection(target);
}
and then for your tag you could do
public class YourTag extends TagSupport {
#Autowired
private SomeBean someBean;
public YourTag() {
YourHelperClass.autowireAllFor(this);
}
}
The obvious disadvantage of this approach is that you have to do this for every constructor, but as TagSupport only has one, it should not be a problem. You can go even one step further and create a helper superclass which always guarantees autowiring:
public class SpringTagSupport extends TagSupport {
public SpringTagSupport() {
super();
YourHelperClass.autowireAllFor(this);
}
}
The rest is as easy as extending your classes from SpringTagSupport.
First I write this:
public abstract class SpringSuportedTag extends SimpleTagSupport{
protected WebApplicationContext _applicationContext;
protected WebApplicationContext getSpringContext(){
PageContext pageContext = (PageContext) getJspContext();
if(_applicationContext==null){
_applicationContext = RequestContextUtils.getWebApplicationContext(
pageContext.getRequest(),
pageContext.getServletContext()
);
initCustomBeans();
}
return _applicationContext;
}
protected abstract void initCustomBeans();
/**
* Deprecated for inserting extra logic. Use {#link #doTagWithSpring()} instead.
*/
#Override
#Deprecated
public void doTag() throws JspException, IOException {
getSpringContext();
doTagWithSpring();
}
abstract void doTagWithSpring() throws JspException, IOException;
}
And usage:
public class SlotTag extends SpringSuportedTag {
// #Resource(name="userDetailHolder")
// not work here
UserDetailHolder userDetail;
private String slotname;
public String getSlotname() {
return slotname;
}
public void setSlotname(String slotname) {
this.slotname = slotname;
}
#Override
void doTagWithSpring() throws JspException, IOException {
PageContext pageContext = (PageContext) getJspContext();
String userDetailCode = pageContext.getAttribute(InitWidgetUserTag.KAY_USERDETAIL, PageContext.PAGE_SCOPE).toString();
userDetail.init(userDetailCode);
String pageID = pageContext.getAttribute(InitWidgetUserTag.KAY_PAGEID, PageContext.PAGE_SCOPE).toString();
getJspContext().getOut().println("<b>slot for user:"+userDetail.getUserId()+"</b>");
}
#Override
protected void initCustomBeans() {
userDetail = (UserDetailHolder) getSpringContext().getBean("userDetailHolder");
}
}
It's work.
But than i found this:
Spring supported Tag Libraries. Truth in my progect I still use own solution.
Use :-
import org.springframework.web.servlet.tags.RequestContextAwareTag;
public class FetchTagNameTag extends RequestContextAwareTag {
// #Autowired
// private TaskService taskService;
#Override
protected int doStartTagInternal() throws Exception {
TaskService taskService= getRequestContext().getWebApplicationContext().getBean(TaskService.class);
return 0;
}