Here is backing bean of my log in page. Why does it preserve values after calling the logIn() method? I mean after I submit the form and return to the login page my previously entered username is already there. I thought that by using #RequestScoped annotation the values are not preserved.
#Controller
#ManagedBean
#RequestScoped
public class LogInBean implements Serializable {
private static final long serialVersionUID = 2092611147930386873L;
#Autowired
private UserService userService;
private String username;
private String password;
private boolean rememberMe;
public String logIn() {
return "index?faces-redirect=true";
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isRememberMe() {
return rememberMe;
}
public void setRememberMe(boolean rememberMe) {
this.rememberMe = rememberMe;
}
}
It's because you're actually using an application scoped Spring managed bean instead of a request scoped JSF managed bean.
Your main mistake is that you're mixing JSF bean management annotations with a Spring bean management annotation and somehow are expecting that they seamlessly understand each other.
#Controller
#ManagedBean
#RequestScoped
This is thus not true. You effectively end up with two completely independent managed bean instances, one managed by JSF and another managed by Spring. The #RequestScoped annotation is specific to JSF bean management facility. The Spring's equivalent, #Scope("request") is absent and therefore the Spring managed bean defaults to the application scope. When referencing the bean in EL scope like so #{logInBean}, the Spring managed one gets precedence over JSF managed one because of the Spring bean EL resolver and you ultimately end up with an application scoped Spring managed bean. This totally explains the symptoms.
Fix it accordingly by getting rid of JSF bean management annotations and placing the desired Spring scope annotation:
#Controller
#Scope("request")
(an alternative would be to get rid of Spring bean management annotation and replace #Autowired by #EJB, like as one would normally do when using standard Java EE stack without any 3rd party library)
If you still face this problem, then another possible cause would be that you're submitting the login synchronously while not having autocomplete="off" on the input fields, head to this Q&A then: Disable web browser password save.
Related
I want to write some kind of unit test which depends on Spring Security.
For example, I have some service method which uses some repository and marked with #PreAuthorize annotation. Repository I can mock with Mockito, there is no problem. Also I can mock Security Context by #WithSecurityContext annotation. But when I run test, the #PreAuthorize annotation is just ignored. Of course I can run that test with #SpringBootTest annotation as an integration test and in this case the Security Context is up but this way is heavy and slow.
Is there a way to run unit test with only Spring Security Context raised?
UPDATE
Made an example of such kind of test. Thanks to #Sam Brannen for giving right direction.
#ActiveProfiles("method-security-test")
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {ExampleService.class, ExampleServiceTest.MethodSecurityConfiguration.class})
public class ExampleServiceTest {
private ExampleService service;
#Autowired
public void setService(ExampleService service) {
this.service = service;
}
#Test
#WithMockUser(username = "john_doe")
public void testAuthenticated() {
String actualMessage = service.example();
Assert.assertEquals("Message of john_doe", actualMessage);
}
#Test(expected = AuthenticationException.class)
public void testNotAuthenticated() {
service.example();
Assert.fail();
}
#TestConfiguration
#EnableGlobalMethodSecurity(prePostEnabled = true)
static class MethodSecurityConfiguration extends GlobalMethodSecurityConfiguration {
}
}
#Service
class ExampleService {
#PreAuthorize("isAuthenticated()")
String example() {
Principal principal = SecurityContextHolder.getContext().getAuthentication();
return "Message of " + principal.getName();
}
The #PreAuthorize annotation from Spring Security will only be honored if Spring Security proxies your component (e.g., service bean).
The simplest way to make that happen is by annotating an #Configuration class with #EnableGlobalMethodSecurity(prePostEnabled = true), having your component registered as a bean (e.g., via component scanning or an #Bean method), and including your AuthenticationManager setup.
You can then create a focused integration test using #ContextConfiguration (without Spring Boot testing support) to load an ApplicationContext from your #Configuration class. And you can use #Autowired to get access to your proxied component which will be advised with the #PreAuthorize security check support.
You might find this old blog post useful as well for background information: https://spring.io/blog/2013/07/04/spring-security-java-config-preview-method-security/
I want to using #PreAuthorize with SpEL within Spring Security like the example at http://forum.spring.io/forum/spring-projects/security/100708-spel-and-spring-security-3-accessing-bean-reference-in-preauthorize
but this is not work for me while using it in Spring Security 4.1.4. Below is my sample code:
A bean class:
package com.service.spel;
import org.springframework.stereotype.Component;
#Component(value="accessBean")
public class AccessCheckBean {
public String getPermision(){
/**
* return the api permision
*/
String scope = "#oauth2.hasScope('resource.Update')";
return scope;
}
}
In controller:
#PreAuthorize("accessBean.getPermision()")
#GetMapping("/perm")
public #ResponseBody String getPerm(){
return "Perm";
}
Error message:
Failed to evaluate expression 'accessBean.getPermision()'
seems I can't use SpEL like above, then if I am using this version of Spring Security, how can I proceed?
You have to use #, see Spring Security Reference:
Referring to Beans in Web Security Expressions
If you wish to extend the expressions that are available, you can easily refer to any Spring Bean you expose. For example, assuming you have a Bean with the name of webSecurity that contains the following method signature:
public class WebSecurity {
public boolean check(Authentication authentication, HttpServletRequest request) {
...
}
}
You could refer to the method using:
<http>
<intercept-url pattern="/user/**"
access="#webSecurity.check(authentication,request)"/>
...
</http>
or in Java configuration
http
.authorizeRequests()
.antMatchers("/user/**").access("#webSecurity.check(authentication,request)")
...
i have a strange behaviour in my app:
I use a SessionScope bean (Bean A) to hold users preferences. In my other Bean (Bean B), which is in RequestScope, I inject the SessionScope bean.
Bean B has a #PostConstruct method to retrieve a list of values from the database depending on the value in Bean A. The application gets confused when the user changes the value in Bean A and its value in Bean B is not correct at the time #PostConstruct method is invoked. I tested it with logs.
I think all setter methods will be updated before Invoke Application Phase?
Here is a code sample:
Bean A:
#Named
#SessionScoped
public class SessionBean implements Serializable {
private static final long serialVersionUID = -4214048619877179708L;
#Inject private Logger log;
private BankAccount selectedBankAccount;
public BankAccount getSelectedBankAccount() {
return selectedBankAccount;
}
public void setSelectedBankAccount(BankAccount selectedBankAccount) {
log.info("ba: " + selectedBankAccount);
this.selectedBankAccount = selectedBankAccount;
}
Bean B:
#RequestScoped
public class SubAccountListProducer {
#Inject private SessionBean sessionBean;
#Inject private EntityManager em;
#PostConstruct
public void retrieveAllSubAccount() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<SubAccount> criteria = cb.createQuery(SubAccount.class);
Root<SubAccount> account = criteria.from(SubAccount.class);
log.info("retrieveAllSubAccount: " + sessionBean.getSelectedBankAccount());
criteria.select(account).where(cb.equal(account.get("bankAccount"), sessionBean.getSelectedBankAccount()));
criteria.select(account).orderBy(cb.desc(account.get("name")));
entityList = em.createQuery(criteria).getResultList();
}
Sample logs:
ba: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
retrieveAllSubAccount: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
retrieveAllSubAccount: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
ba: BankAccount [accountId=987654321, bankName=Barclaycard Barclays Bank, blz=20130600]
As you can see... the first two logs are correct... if the user changes preferences (updates the SessionBean), the view will be rerendered with JSF and the last two logs are not in correct order and my app gets confused.
Thank you for help.
The #PostConstruct is not executed during invoke action phase. It is executed directly after bean's construction. The PostConstruct should only be used to preinitialize some stuff depending on injected dependencies directly after bean's construction. Because your bean is request scoped instead of conversation scoped (or view scoped), it will be constructed on every single request.
You need to do the updating/refreshing job in the real action method instead, which is the method you've specified in the <h:commandButton>/<h:commandLink>. E.g.
<h:commandButton value="Submit" action="#{bean.submit}" />
with
public void submit() {
// ...
retrieveAllSubAccount();
}
I also suggest to put your bean in the CDI conversation scope or JSF view scope, so that it don't unnecessarily get reconstructed on every postback to the same view.
I have the following managed bean which stores the login data after container authentication:
#ManagedBean(name = "authenticatedUserController")
#SessionScoped
public class AuthenticatedUserController implements Serializable {
#EJB
private jpa.UtentiportaleFacade ejbFacade;
public Utentiportale getAuthenticatedUser() {
if (AuthenticatedUser == null) {
Principal principal = FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal();
if (principal != null) {
AuthenticatedUser = ejbFacade.findByLogin(principal.getName()).get(0);
}
}
return AuthenticatedUser;
}
getAuthenticatedUser is called in every page because I put the user name in a facelets template on the top right side.
In PermessimerceController, another managedbean, I need to access login data so it is easy and fast to inject the above session scoped controller:
#ManagedProperty(value = "#{authenticatedUserController}")
private AuthenticatedUserController authenticatedUserController;
I experienced the following problem: trying to access the page which is linked to PermessimerceController without being authenticated I'm redirected to the login page (and this is OK) but after that I get a null pointer exception because authenticatedUserController is null when it is injected inside PermessimerceController.
The page in question uses both PermessimerceController and AuthenticatedUserController so I should guess that for some reason PermessimerceController is created before AuthenticatedUserController. Can you suggest a simple way to solve this problem ?
Alternatively how can I store the login data in an easy to access place ?
Thanks
Filippo
I try to edit this post in the hope to clarify better the problem I have and find useful answers.
Using facelets templating I show the user login name throught a property of AuthenticatedUserController. The rest of the content is linked to PermessimerceController which needs some informations about the user for filtering data. The #ManagedBean annotation is an easy way to accomplish this. Unfortunately if the user access that page without being authenticated the injected AuthenticatedUserController is null. So it seems PermessimerceController is created before AuthenticatedUserController and I wonder why. Is there a trick I can use for being sure AuthenticatedUserController is create before ?
You were apparently accessing it in the bean's constructor:
#ManagedProperty("#{authenticatedUserController}")
private AuthenticatedUserController authenticatedUserController;
public PermessimerceController() {
authenticatedUserController.getAuthenticatedUser(); // Fail!
}
This will indeed not work that way. The bean is constructed before the dependencies are injected (think about it; how else would the dependency injection manager inject it?)
The earliest access point is a #PostConstruct method:
#ManagedProperty("#{authenticatedUserController}")
private AuthenticatedUserController authenticatedUserController;
#PostConstruct
public void init() {
authenticatedUserController.getAuthenticatedUser(); // Success!
}
Currently I am trying to inject a stateless EJB into a CDI managed controller on Jboss 6 AS Final. The controller is managed in the context an accessible from the JSF pages. If I inject the stateless bean with #EJB it is working. If I inject the stateless EJB with #Inject I get the following Exception:
My controller:
#Named("TestController")
public class TestController {
#Inject
private TestManagerLocal myTestManager;
...
}
}
My stateless bean:
#SuppressWarnings("unchecked")
#Stateless
public class TestManagerBean implements TestManagerLocal {
#PersistenceContext
private EntityManager em;
...
}
The Interface of the Bean is annotated with #Local.
If I try to call myTestManager I get the following exception:
WELD-000079 Could not find the EJB in JNDI: class
de.crud.org$jboss$weld$bean-jboss$classloader:id="vfs:$$$usr$local$jboss$server$default$deploy$test$ear"-SessionBean-TestManagerBean_$$_WeldProxy
THX a lot.
For those not having the luxury to change an ear to a war, I've found the following workaround:
Create an EJB in the war
Inject that EJB with the EJBs from the EJB module
Add CDI producer methods
Qualify #Inject with the qualifier for those producer methods:
Code:
// This bean is defined in the WEB module
#Stateless
public class EJBFactory {
#EJB
protected UserDAO userDAO;
// ~X other EJBs injected here
#Produces #EJBBean
public UserDAO getUserDAO() {
return userDAO;
}
// ~X other producer methods here
}
Now EJBs from anywhere in the EAR can be injected with:
// This bean is also defined in the web module
#RequestScoped
public class MyBean {
#Inject #EJBBean
private UserDAO userDAO; // injection works
public void test() {
userDao.getByID(...); // works
}
}
EJBBean is a simple standard qualifier annotation. For completeness, here it is:
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface EJBBean {
}
The problem was, that I built and deployed my application as an ear. Weld is working when I deploy my application as an war including all EJBs.
Currently there are various problems arising from the fact that WARs in EAR-Deployments don't share the same classloader. See https://issues.jboss.org/browse/JBAS-8683 for the ongoing discussion in the JBoss-AS JIRA (and vote it up :-) )
UPDATE I found this information on how to disable separate classloaders, option 1 worked for me, but be extremely careful with this. The separation of classloaders hasn't been introduced for no reason, so apparently there are new problems on the road ahead...