When I am using jsf with spring boot there is no problem to access the jsf bean, but when I add spring security I get access denied 403 when trying to access pages using jsf bean function, I can only access the pages with the url.
I've been searching a lot to solve this issue but nothing did work, please if someone can help me solve this issue.
here is my code:
jsf BeanProduit.java
#ManagedBean
#Component
#SessionScoped
public class BeanProduit {
#Autowired
#Qualifier("produitsPrixServiceImpl")
private CrudService<ProduitsPrix> produitsPrixService;
#Autowired
#Qualifier("produitsStockServiceImpl")
private CrudService<ProduitsStock> produitsStockService;
private List<ProduitsStock> produits;
private Logger logger = Logger.getLogger(getClass().getName());
public BeanProduit() {
produits = new ArrayList<ProduitsStock>();
}
#PostConstruct
public void init() {
produits = getListProductsFinal();
}
public String loadProduct(int codePdt) {
logger.info("loading product: " + codePdt);
try {
// get product from database
ProduitsPrix product = produitsPrixService.findById(codePdt);
// put in the request attribute ... so we can use it on the form page
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
Map<String, Object> requestMap = externalContext.getRequestMap();
requestMap.put("product", product);
} catch (Exception exc) {
// send this to server logs
logger.log(Level.SEVERE, "Error loading product id:" + codePdt, exc);
// add error message for JSF page
addErrorMessage(exc);
return null;
}
return "/pages/form-validation";
}
}
config file of spring security DemoSecurityConfig.java
#EnableWebSecurity
public class DemoSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource securityDataSource;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(securityDataSource);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/assets/**")
.permitAll()
.antMatchers("/authentication/login.xhtml?logout").hasAnyRole("EMPLOYEE")
.antMatchers("/**").hasRole("ADMIN")
.and().formLogin().loginPage("/authentication/login.xhtml")
.loginProcessingUrl("/authenticateTheUser").permitAll()
.defaultSuccessUrl("/", true)
.and().logout().permitAll()
.and().exceptionHandling().accessDeniedPage("/error/error-403.xhtml");
}
}
snippet code from the view
<h:form>
<ui:repeat value="#{beanProduit.produits}" var="produit">
<tr>
<td>#{produit.codePdt}</td>
<td>#{produit.nomPdt}</td>
<td>#{produit.prixPdt}</td>
<td>#{produit.qtePdt}</td>
<td class="text-center">
<h:commandButton class="btn btn-primary" value="Acheter" action="#{beanProduit.loadProduct(produit.codePdt)}" />
</td>
</tr>
</ui:repeat>
</h:form>
Let me start by addressing all of your comment from 2020-06-05 13:41:36Z above:
... if i take off "#ManagedBean" it won't be a bean
Wrong, something 'being' a (java)bean or not has nothing to do with annotations. What it takes for a plain java class to become a javabean can be read here:
What is a JavaBean exactly?
When a java bean becomes a real managed bean, managed by A container (Spring, JSF, CDI, EJB, ...) is a different ballgame
What is difference between JavaBean and ManagedBean
and it cannot return a view.
Wrong again, something, in the jsf sense, being able to 'return a view' or not is dependent on the managed bean (Spring, JSF, CDI, ...) being accessed from a JSF 'page'returning a String or not AND as long as the managed bean is available in the EL resolvers used by facelets/jsf
Navigating to another page JSF
Actually a JSF component on a facelets page is the better description
What is the difference between JSF and Facelets?
What is the difference between JSF, Servlet and JSP?
and if i take of "#Autowired" i cannot use injection right.
Wrong again... #Autowired is the old spring annotation for 'injecting' other managed beans. Other bean managers/containers have the same features.
JSF has (or rather had) #ManagedProperty (long deprecated in comination with its #ManageBean in favour of CDI managed beans), CDI has #Inject with #Named which spring now also supports as an alias to #Autowired and #Component/#Controller
What is the difference between #Inject and #Autowired in Spring Framework? Which one to use under what condition?
Both #Component and #Named for the same bean class
Effectively your misunderstandings from the comment are all addressed in
Spring JSF integration: how to inject a Spring component/service in JSF managed bean?
The last part of your comment
I want to add access to jsf bean if spring security config but i don't know how ?
Wrong again, you access a facelets/jsf page and you (may) control access to it via spring security (or other more independent ways like JBoss/RedHat KeyCloak or Apache Shiro or the standardized javaee-8 security api
So why is this not an answer to your question (but I'll leave it here anyway). In your question you state
I cannot access pages using bean, I can only access the pages with the url.
Then debug this... Set breakpoints, are there redirects, any errors, how does the url look like when you type it in, how does it look like when returned from method call to a bean, with and without spring security. Those are relevant details. Make a real 'https://stackoverflow.com/help/minimal-reproducible-example'
And lastly
I've been searching a lot to solve this issue but nothing did work,
How to ask states to search and keep track and unfortunately it only implies to mention in the question what you found (if you don't mention it, it cannot help the ones who try to help you in narrowing things down). and 'nothing did work' is not the best description. Did you get 404's? 500's?
I was missing this line in the form:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
Related
I have a Spring Boot application that is running. As soon as I added Spring Security, the app generated an error.
I have a form that is backed by a bean. When I enable Spring Security, the bean for the form cannot be found. Before I added Spring Security, the bean and form worked.
The error that I receive after making a GET request to the form is
Neither BindingResult nor plain target object for bean name 'orderActive' available as request attribute
The form is using the ThymeLeaf package.
Spring Security Configuration
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("buzz")
.password("{noop}infinity")
.authorities("ROLE_USER");
}
}
Controller Method
#GetMapping("/orders/current")
public String orderForm() {
return "orderForm";
}
Test Class Annotations
#SpringBootTest
#AutoConfigureMockMvc
class DesignTacoControllerTest {
Test Method
#WithMockUser("buzz")
#Test
public void testProcessDesignGet() throws Exception {
mockMvc.perform(get("/orders/current")
.requestAttr("orderActive", new Order()))
.andExpect(status().isOk());
}
orderForm
<form method="POST" th:action="#{/orders}" th:object="${orderActive}">
I have tried adding a RequestAttribute to the controller method.
#GetMapping("/orders/current")
public String orderForm(#RequestAttribute("orderActive") Order orderActive) {
return "orderForm";
}
When I debug, the order has the same ID as the one that was added in the test method. The next step is to render the view. When I continue, the error appears.
Somewhere between the controller method and the view, the request parameter disappears. It has something to do with security, since the code runs without security enabled. The order form is found, so the page is not forbidden. Does security disable the request attributes?
You say it worked before Security, but, do you have a class (DTO) OrderForm with the fields you need in your form? I don't see one. If you don't create one and then add it to the model (that's the Binding part):
#GetMapping("/orders/current")
public String orderForm(Model model) {
model.addAttribute("orderForm", new OrderForm())
return "orderForm";
}
I have several controller functions separated by role, and instead of doing role validation in each controller method, I found that it seems to be able to get done by using Aspect, however something isn't right in my implementation as the code in Aspect never runs
Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface ForMerchantOnly {}
Aspect:
#Aspect
#Configuration
public class ForMerchantOnlyAspect {
private static final Logger logger = LogManager.getLogger(ForMerchantOnlyAspect.class);
#Before("#annotation(com.example.api.annotation.ForMerchantOnly) && args(request)")
public void before(HttpServletRequest request) throws ServiceException {
if (!(request instanceof HttpServletRequest)) {
throw new RuntimeException("request should be HttpServletRequesttype");
}
String domain = request.getServerName();
System.out.println("Aspect showing domain " + domain);
// -- other code
}
}
Controller
#ForMerchantOnly
#GetMapping("/list")
public ResponseEntity<ApiResp> list() {
System.out.println("Show something");
return ResponseEntity.ok().body();
}
I'm assuming when i call controller /list method via chrome browser, it would hit the code in ForMerchantOnlyAspect but it just went into the controller method directly. Am I missing something?
The Aspect was not working as it could not find a matching joinpoint . There are no controller methods that has annotation #ForMerchantOnly and has an argument of type HttpServletRequest
From the documentation :
args: Limits matching to join points (the execution of methods when
using Spring AOP) where the arguments are instances of the given
types.
Following aspect may be used for the requirement . Scoping designator within will set the scope to advice.
#Before("#annotation(com.example.api.annotation.ForMerchantOnly) && within(com.example.api..*)")
public void before() {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes())
.getRequest();
System.out.println("Aspect showing domain " + request.getServerName());
}
Also note that an Aspect is better annotated with #Component and #Configuration be used for configurations.
You may also have a look at Method Security of Spring security framework , which lets to secure a method with annotations.
From the documentation
From version 2.0 onwards Spring Security has improved support
substantially for adding security to your service layer methods. It
provides support for JSR-250 annotation security as well as the
framework’s original #Secured annotation. From 3.0 you can also make
use of new expression-based annotations. You can apply security to a
single bean, using the intercept-methods element to decorate the bean
declaration, or you can secure multiple beans across the entire
service layer using the AspectJ style pointcuts.
Usually, i'm accessing business objects (services) with #ManagedProperty in a main class annotated with #ManagedBean.
Here, i have a Servlet and i want to inject my business objects.
The use of managedProperty doesn't work with the annotation #WebServlet.
When i use WebApplicationContext, it's working.
Does the use of WebApplicationContext is the clean way to do that or is there a cleaner method to do it ?
#WebServlet(urlPatterns ="/Graphic")
public class GraphicServlet extends HttpServlet {
// Method 1 - This is not working ( the usual way in a ManagedBean)
#ManagedProperty(value="#{participantBo}")
private ParticipantBo participantBo; // getters and setters added
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// Method 2 - This is working
WebApplicationContext springContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
ParticipantBo participantBo = (ParticipantBo) springContext.getBean("participantBo");
List<Participant> myParticipants = participantBo.findAllParticipant();
for (Participant participant : myParticipants) {
System.out.println(participant.getId_study());
}
}
}
Using WebApplicationContext is one of the ways to access the Spring web context from where you want. In fact, if you are able to reach Spring beans using #ManagedProperty, it means you have tied JSF and Spring frameworks in some way. #ManagedProperty should work only for JSF managed beans. However, being your servlet totally independent from the framework, accessing the Spring context this way is a choice.
See also:
https://stackoverflow.com/a/12244697/1199132
Spring Integration with other frameworks
I am using JSF Mojarra 2.1.13, PrimeFaces 3.5 and Spring 3.2.3 for my application. For DI I am using Spring approach (not CDI). I am following the tutorial on PrimeFaces demos with the collector: http://www.primefaces.org/showcase/ui/collector.jsf
Everything is working fine, I can add my values to the list, get them, etc.. The problem is that for e.g. if I open two browsers and adding some values into the list, then in another browser I am adding a few values as well, and if I refresh browsers I am seeing the all the values which has been entered in both browsers. So if I enter two values in one browser two in the other, after refreshing them I am seeing four values in both browsers. I want my values to not be shared across different sessions.
My bean looks like this:
#Component
#ManagedBean
public class ClientBean extends BaseBean {
private Client client = new Client();
private List<Client> clients = new LinkedList<>();
public String reInit() {
client = new Client();
return null;
}
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
public List<Client> getClients() {
return clients;
}
public void setClients(List<Client> clients) {
this.clients = clients;
}
}
I know that I am creating global variables:
private Client client = new Client();
private List<Client> clients = new LinkedList<>();
But this is showed in tutorial. So how can I handle this situation to make collector work so that those variables won't be shared across different sessions?
EDIT
I have tried to annotate my bean with: #RequestScoped or #SessionScoped - didn't work. The same problem remains.
Not really sure why you configured the #ManagedBean as a #Component to begin with. This problem is because Spring handles a single instance of #Component across your application (or at least that's how it looks from your explanation). Remove it and use #ViewScoped in your managed bean to make this work as expected. Note that if you use Spring to manage your JSF managed beans, then you have to add this configuration in your faces-config.xml (from mkyong tutorial):
<application>
<el-resolver>
org.springframework.web.jsf.el.SpringBeanFacesELResolver
</el-resolver>
</application>
But doing this you will loss the power of #ViewScoped managed beans. To solve this error, you have to implement the #ViewScoped in Spring. There are plenty examples on the net about this, and looks that the most popular is from Cagatay's
More info about JSF managed bean scopes: Communication in JSF 2: Managed bean scopes
guys i'm using jsf 2.0 with spring.
I have annotated a method in a managed bean with #PostConstruc, but if in the bean there aren't field connected to the jsf page the #PostConstruct method isn't called even if in the jsf page there is an action method connected to the Bean.
Thank you in advance.
Added code for explaination:
this si my BackingManagedBean
#ManagedBean(name="utenteBean")
#ViewScoped
public class UtenteBean extends SupportBean implements Serializable
While this is my ControllerManagedBean
#ManagedBean(name="gestisciUtentiController")
#ViewScoped
public class GestisciUtentiController extends MessageSupportBean implements Serializable {
#ManagedProperty(value="#{utenteBean}")
private UtenteBean utenteBean;
public void setUtenteBean(UtenteBean utenteBean) {
this.utenteBean = utenteBean;
}
#PostConstruct
public void loadBean()
{
try
{
utenteBean.setUtentis(getFacadeFactory().getUtenteFacade().readAllOrdered(Utente.class, "username"));
}
catch (ApplicationException e)
{
setExceptionMessage(e.getLocalizedMessage(), e.getLocalizedMessageDetail());
}
}
http://blog.icefaces.org/blojsom/blog/default/2009/04/23/Making-distinctions-between-different-kinds-of-JSF-managed-beans/ i'm trying to use this approch. You think that that approch isn't correct? –
I'm not sure. That article mentions that the model is typically to be placed in the session scope. This is actually a poor approach. Injecting a session scoped bean in a request scoped bean makes sense if the session scoped one is for example the logged-in user and the request scoped one is bound to the form.
In your case you should just make the model bean a property of the controller bean and use #{gestisciUtentiController.utenteBean.someProperty} instead of #{utenteBean.someProperty}.
I've some "JSF design" questions before, you may find them useful as well:
JSF MVC design question
JSF / Java Design Question
What components are MVC in JSF MVC framework?