Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spring http security #9

Open
BaranovNikita opened this issue Apr 16, 2020 · 16 comments
Open

Spring http security #9

BaranovNikita opened this issue Apr 16, 2020 · 16 comments

Comments

@BaranovNikita
Copy link

Hello! I'm try to use spring security for some methods of my REST controller. But i have 403 error all time. My security config:
image
in web.xml:
image

Or Is it possible to protect some methods? Thanks!

@ngriffin7a
Copy link
Member

@BaranovNikita Are your REST controller endpoints packaged within a PortletMVC4Spring based WAR project? If so, is it being deployed to Liferay, Pluto, or some other portal?

@BaranovNikita
Copy link
Author

@BaranovNikita Are your REST controller endpoints packaged within a PortletMVC4Spring based WAR project? If so, is it being deployed to Liferay, Pluto, or some other portal?

Yes, ofcourse. I have a WAR artifact that deployed in Liferay 7.3.0 portal

@ngriffin7a
Copy link
Member

@BaranovNikita When you get an opportunity, please paste an example of a URL that is used to invoke the REST endpoint. Also, could you paste a link to an SSCCE that I can build with Maven or Gradle and deploy locally in order to try and reproduce the problem?

@BaranovNikita
Copy link
Author

BaranovNikita commented Apr 19, 2020

@BaranovNikita When you get an opportunity, please paste an example of a URL that is used to invoke the REST endpoint. Also, could you paste a link to an SSCCE that I can build with Maven or Gradle and deploy locally in order to try and reproduce the problem?

security-test.zip

Hello! I'm try create simple project with this error, but I'm change Order property for Security Config and got other error (CSRF). Can you check it, please? Thanks

@ngriffin7a
Copy link
Member

@BaranovNikita Thank you for providing the SSCCE. After commenting out the <parent>...</parent> part from pom.xml, I was able to get the WAR artifact to build with mvn clean package. It also deploys fine to Liferay Portal CE 7.3 GA2.

However, I cannot add the portlet named security-test to a portal page without seeing the following error in the console log:

com.liferay.portletmvc4spring.NoHandlerFoundException: No handler found for portlet request: mode 'view', phase 'RENDER_PHASE', parameters map[[empty]]

Having said that, I think the main problem you are describing is not a portlet-related problem but a instead it is a problem with usage of @RestController

Please paste an example of a URL that is used to invoke the REST endpoint so that I can try to reproduce the problem. Thanks.

@BaranovNikita
Copy link
Author

@BaranovNikita Thank you for providing the SSCCE. After commenting out the <parent>...</parent> part from pom.xml, I was able to get the WAR artifact to build with mvn clean package. It also deploys fine to Liferay Portal CE 7.3 GA2.

However, I cannot add the portlet named security-test to a portal page without seeing the following error in the console log:

com.liferay.portletmvc4spring.NoHandlerFoundException: No handler found for portlet request: mode 'view', phase 'RENDER_PHASE', parameters map[[empty]]

Having said that, I think the main problem you are describing is not a portlet-related problem but a instead it is a problem with usage of @RestController

Please paste an example of a URL that is used to invoke the REST endpoint so that I can try to reproduce the problem. Thanks.

I dont wanna use it for visible portlet. Only for REST Spring service. In this sample we have 2 endpoints:
/o/security-test/test/ping (GET) - this endpoint not secured, work fine
/o/security-test/test/secured (POST) - secured, but not work

@ngriffin7a
Copy link
Member

@BaranovNikita I think I know the reason why your TestController.secure(String) method was not being invoked. You need to specify the CSRF token in order for the Spring security framework to call through to a controller annotated with @RequestMapping(method = RequestMethod.POST). See https://docs.spring.io/spring-security/site/docs/3.2.0.CI-SNAPSHOT/reference/html/csrf.html#csrf-include-csrf-token

@ngriffin7a
Copy link
Member

Here is a new project that I created that shows how to make it work -- it defines a portlet with a JSP that has test URLs that you can invoke:

com.liferay.test.portletmvc4spring.portlet-src.zip

Also, please take special note of the filter-mapping entries in web.xml:

	<filter-mapping>
		<filter-name>delegatingFilterProxy</filter-name>
		<url-pattern>/WEB-INF/servlet/view</url-pattern>
		<dispatcher>FORWARD</dispatcher>
		<dispatcher>INCLUDE</dispatcher>
	</filter-mapping>
	<filter-mapping>
		<filter-name>delegatingFilterProxy</filter-name>
		<url-pattern>/test/*</url-pattern>
		<dispatcher>ERROR</dispatcher>
		<dispatcher>REQUEST</dispatcher>
	</filter-mapping>

I had to create two mappings in order to distinguish between the @RestController and the portlet @Controller.

@ngriffin7a
Copy link
Member

@BaranovNikita Note that the reason why CSRF is enforced is because you specified it in the configuration:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf()
                .disable()
                .cors()
                .disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/test/secured").hasRole("administrator")
                .antMatchers(HttpMethod.GET, "/test/ping").hasRole("guest");
    }

@BaranovNikita
Copy link
Author

@BaranovNikita Note that the reason why CSRF is enforced is because you specified it in the configuration:

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf()
                .disable()
                .cors()
                .disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/test/secured").hasRole("administrator")
                .antMatchers(HttpMethod.GET, "/test/ping").hasRole("guest");
    }

but..I use disable() for that?

@ngriffin7a
Copy link
Member

ngriffin7a commented Apr 25, 2020

@BaranovNikita You're right, I didn't see the call to disable().

Do you have the following bean defined in one of your Spring XML config files?

<bean id="springSecurityPortletConfigurer" class="com.liferay.portletmvc4spring.security.SpringSecurityPortletConfigurer" />

If so, then CSRF will be enabled because of the following:
https://github.com/liferay/portletmvc4spring/blob/master/security/src/main/java/com/liferay/portletmvc4spring/security/SpringSecurityPortletConfigurer.java#L94

If that's the case, then you could define your RestSecurityConfig like the following:

<bean id="springSecurityPortletConfigurer" class="portlet1.configuration.RestSecurityConfig" />
<bean id="delegatingFilterProxy" class="org.springframework.web.filter.DelegatingFilterProxy">
	<property name="targetBeanName" value="springSecurityFilterChain" />
</bean>

(Note that in my testing I had to put it just before the delegatingFilterProxy)

However, there is a problem with your usage of the following:

http.authorizeRequests()
	.antMatchers(HttpMethod.POST, "/test/secured").hasRole("administrator")
	.antMatchers(HttpMethod.GET, "/test/ping").hasRole("guest");

Background: You are trying to use a Spring REST controller like you would in a typical webapp environment. PortletMVC4Spring portlet applications are invoked with PortletURLs, which causes the portlet lifecycle to be invoked using PortletRequests. Spring REST goes through a servlet, meaning it does not go through the portlet lifecycle. You are basically using the Servlet API in order to receive HttpServletRequests from Liferay's Web Application Bundle (WAB) Extender, just like you would with a WAR deployed in plain Tomcat.

The problem is that the call to authorizeRequests() requires that there be some kind of underlying authentication -- and to my knowledge, there is no existing authentication that knows how to ask Liferay who the current user is, and whether or not they have been authenticated.

So... I decided to try and make this work. Attached you will find com.liferay.test.portletmvc4spring.portlet-try2-src.zip which is an updated project with some new source code. The configure(HttpSecurity) method now looks like this:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .csrf()
            .disable()
            .cors()
            .disable()
            .anonymous()
            .disable()
            .apply(new LiferayConfigurer<>());

    http
            .authorizeRequests()
            .antMatchers(HttpMethod.POST, "/test/secured").hasRole("Administrator")
            .antMatchers(HttpMethod.GET, "/test/ping").hasRole("Guest");
}

(BTW, I changed the role names to contain an UPPER-case first letter: Administrator instead of administrator, and Guest instead of guest)

And there is some new source code for LiferayConfigurer.java and some associated classes.

When you get an opportunity, please build this project and give it a try in your environment. If it works, then we can publish a new JAR in the PortletMVC4Spring project that contains the LiferayConfigurer. That way you would be able to reuse it in multiple projects.

Kind Regards,

Neil

@ngriffin7a
Copy link
Member

@BaranovNikita
Copy link
Author

BaranovNikita commented Apr 26, 2020

Thanks for that! May be you know, how I can do request from client side?
I try use
Liferay.Util.fetch('/o/com.liferay.test.portletmvc4spring.portlet/test/secured', { method: 'POST', body: JSON.stringify({ test: 'test'}), headers: { 'Content-Type': 'application/json' } })
But have 403 error :(

My user roles:
image

@ngriffin7a
Copy link
Member

You might need to use an absolute URL that contains the scheme, server, port etc like "http://localhost:8080/o/com.liferay.test.portletmvc4spring.portlet/test/secured"

Here is an example that I recently got to work in portlet JSP:
https://github.com/ngriffin7a/liferay-revived-modules/blob/master/modules/powwow/powwow-web/src/main/resources/META-INF/resources/meetings/view.jsp#L159

You're probably not using JSP from within a portlet view so you wouldn't be able to use JSP tags like <portlet:actionURL/> etc so you would have to determine the scheme, server, port a different way.

Please let me know if you get things working and I start scheduling a time to publish a new JAR in the PortletMVC4Spring project that contains the LiferayConfigurer.

Kind Regards,

Neil

@BaranovNikita
Copy link
Author

You might need to use an absolute URL that contains the scheme, server, port etc like "http://localhost:8080/o/com.liferay.test.portletmvc4spring.portlet/test/secured"

Here is an example that I recently got to work in portlet JSP:
https://github.com/ngriffin7a/liferay-revived-modules/blob/master/modules/powwow/powwow-web/src/main/resources/META-INF/resources/meetings/view.jsp#L159

You're probably not using JSP from within a portlet view so you wouldn't be able to use JSP tags like <portlet:actionURL/> etc so you would have to determine the scheme, server, port a different way.

Please let me know if you get things working and I start scheduling a time to publish a new JAR in the PortletMVC4Spring project that contains the LiferayConfigurer.

Kind Regards,

Neil

Hello! I have small fix your code. I check User in request, not ThemeDisplay. May be it not work in JSP/Thymeleaf... But it work for simple REST controller :) Thank you very match!

LiferayAuthentication.java.zip

@ahujadipak3
Copy link

Hi @ngriffin7a ,

Thanks for the example ('com.liferay.test.portletmvc4spring.portlet-try2-src.zip') attached above. However, you're invoking the REST endpoints from JSP (or withing session scope). I would like to have it invoke from client (say Postman, or third party), so what kind of changes we would need to do in LiferayAuthentication.java? We want to extract user information from JWT token as part of JWT authentication.

Is there any plan to add LiferayConfigurer and other classes you wrote in distrubution?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants