How to autowire Spring bean in Wicket Session class? - java

I have a Wicket Session class as follows
public class IASession extends AuthenticatedWebSession {
private static final long serialVersionUID = 3529263965780210677L;
#SpringBean
private UserService userService;
public IASession(Request request) {
super(request);
}
#Override
public boolean authenticate(String username, String password) {
// Get the user
UserDetailsDTO user = userService.findByEmail(username);
if(null != user && user.getPassword().equals(password))
return true;
else
return false;
}
#Override
public Roles getRoles() {
Roles roles = new Roles();
roles.add("SIGNED_IN");
return roles;
}
}
In this class, I am trying to autowire Spring service using wicket-spring annnotation #SpringBean. But when I am trying to login, it giving me error.
Last cause: null
WicketMessage: Method onFormSubmitted of interface org.apache.wicket.markup.html.form.IFormSubmitListener targeted at [StatelessForm [Component id = login-form]] on component [StatelessForm [Component id = login-form]] threw an exception
Wicket is unable to autowire the userService spring bean and that is why it's null.
What can I do to fix this?

Since the Session is not a Component or Behavior you'll have to overwrite the constructor and call Injector.get.inject(this). See the SpringComponentInjector doc.
public IASession(Request request) {
super(request);
Injector.get().inject(this);
}

Related

Getting user info in java RESTful resource

I have a Rest based service using a ContianerRequestFilter (AuthFilter below) to validate a user or their token. Everything at that level works fine as the user is authorized or not authorized as expected. The question is how to do get the user info in the resource layer? For instance if a user requests a list of areas in AreasResource (below), how can I get the user info and use that to constrain the results return to him/her?
AuthFilter:
#Provider
#PreMatching
public class AuthFilter implements ContainerRequestFilter
{
#Autowired
IAuthenticator authenticator;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
//PUT, POST, GET, DELETE...
String method = requestContext.getMethod();
String path = requestContext.getUriInfo().getPath(true);
UserWrapper authenticationResult = null;
Date expireTime = new Date(new Date().getTime() + 60 * 1000);
if (!"init".equals(path))
{
if ("GET".equals(method) && ("application.wadl".equals(path) || "application.wadl/xsd0.xsd".equals(path)))
{
return;
}
String auth = requestContext.getHeaderString("authorization");
if(auth == null)
{
throw new WebApplicationException(Status.UNAUTHORIZED);
}
if (auth.startsWith("Bearer"))
{
String token = auth.substring("Bearer".length()).trim();
try
{
authenticationResult = validateToken(token);
}
catch (Exception e)
{
throw new WebApplicationException(Status.UNAUTHORIZED);
}
}
else
{
//lap: loginAndPassword
String[] lap = BasicAuth.decode(auth);
if (lap == null || lap.length != 2)
{
throw new WebApplicationException(Status.UNAUTHORIZED);
}
// Handle authentication validation here
authenticationResult = authenticator.authenticatUser(lap);
// if null then user can't be found or user name and password failed
if (authenticationResult == null)
{
throw new WebApplicationException(Status.UNAUTHORIZED);
}
}
}
else
{
authenticationResult = new UserWrapper(new User(), expireTime.getTime());
}
// We passed so we put the user in the security context here
String scheme = requestContext.getUriInfo().getRequestUri().getScheme();
requestContext.setSecurityContext(new ApplicationSecurityContext(authenticationResult, scheme));
}
private UserWrapper validateToken(String token) throws Exception
{
UserWrapper userWrapper = AuthenticatorCache.getInstance().getObj(token);
if (userWrapper == null)
{
throw new Exception("No session found");
}
return userWrapper;
}
}
Areas Resource:
#Path("/areas")
#Component
#Api(value = "/areas" )
public class AreasResource implements IAreas
{
#Override
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response listActiveAreas() {
return Response.ok('woo hoo it worked').build();
}
}
Overriding the SecurityContext
One possible way to achieve it is overriding the SecurityContext of the ContainerRequestContext in your ContainerRequestFilter implementation. It could be something as following:
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return username;
}
};
}
#Override
public boolean isUserInRole(String role) {
return true;
}
#Override
public boolean isSecure() {
return false;
}
#Override
public String getAuthenticationScheme() {
return null;
}
});
Then the SecurityContext can be injected in any resource class using the #Context annotation:
#Path("/example")
public class MyResource {
#Context
private SecurityContext securityContext;
...
}
It alson can be injected in a resource method parameter:
#GET
#Path("/{id}")
#Produces(MediaType.APPLICATION_JSON)
public Response myResourceMethod(#PathParam("id") Long id,
#Context SecurityContext securityContext) {
...
}
And then get the Principal from the SecurityContext:
Principal principal = securityContext.getUserPrincipal();
String username = principal.getName();
I have initially described this approach in this answer.
Alternatives
If you don't want to override the SecurityContext for some reason, you could consider other approaches, depending on what you have available in your application:
With CDI, you could create a bean annotated with #RequestScoped to hold the name of the authenticated user. After performing the authentication, set the name of the user in the request scoped bean and inject it into your resource classes using #Inject.
Since you are using Spring, you could consider using Spring Security on the top of your JAX-RS application for authentication and authorization.
I guess you need to add your SecurityContext as parameter to the method annotated as #Context like that:
#Override
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response listActiveAreas(#Context SecurityContext securityCtx) {
// Do something with data in securityCtx...
return Response.ok("woo hoo it worked").build();
}
If it will not work (I did not try it and use other way):
You may set your securityContext as HttpRequest/HttpServletRequest or (Session) attribute with some name (as example user.security.ctx) and inject Request same way. i.e.
#Override
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response listActiveAreas(#Context HttpServletRequest request) {
SecurityContext securityCtx = (SecurityContext )request.getAttribute("user.security.ctx");
// Do something with data in securityCtx...
return Response.ok("woo hoo it worked").build();
}

Spring MVC validator annotation + custom validation

I'm working on spring mvc application, where I should aplly validation based on Spring MVC validator. I first step for that I added annotation for class and setup controller and it works fine. And now I need to implement custom validator for perform complex logic, but i want to use existing annotation and just add additional checking.
My User class:
public class User
{
#NotEmpty
private String name;
#NotEmpty
private String login; // should be unique
}
My validator:
#Component
public class UserValidator implements Validator
{
#Autowired
private UserDAO userDAO;
#Override
public boolean supports(Class<?> clazz)
{
return User.class.equals(clazz) || UsersForm.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors)
{
/*
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "NotEmpty.user");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "login", "NotEmpty.user");
*/
User user = (User) target;
if (userDAO.getUserByLogin(user.getLogin()) != null) {
errors.rejectValue("login", "NonUniq.user");
}
}
}
My controller:
#Controller
public class UserController
{
#Autowired
private UserValidator validator;
#InitBinder
protected void initBinder(final WebDataBinder binder)
{
binder.setValidator(validator);
}
#RequestMapping(value = "/save")
public ModelAndView save(#Valid #ModelAttribute("user") final User user,
BindingResult result) throws Exception
{
if (result.hasErrors())
{
// handle error
} else
{
//save user
}
}
}
So, Is it possible to use custom validator and annotation together? And if yes how?
I know this is a kind of old question but, for googlers...
you should use addValidators instead of setValidator. Like following:
#InitBinder
protected void initBinder(final WebDataBinder binder) {
binder.addValidators(yourCustomValidator, anotherValidatorOfYours);
}
PS: addValidators accepts multiple parameters (ellipsis)
if you checkout the source of org.springframework.validation.DataBinder you will see:
public class DataBinder implements PropertyEditorRegistry, TypeConverter {
....
public void setValidator(Validator validator) {
assertValidators(validator);
this.validators.clear();
this.validators.add(validator);
}
public void addValidators(Validator... validators) {
assertValidators(validators);
this.validators.addAll(Arrays.asList(validators));
}
....
}
as you see setValidator clears existing (default) validator so #Valid annotation won't work as expected.
If I correctly understand your problem, as soon as you use you custom validator, default validation for #NotEmpty annotation no longer occurs. That is common when using spring : if you override a functionnality given by default, you have to call it explicitely.
You have to generate a LocalValidatorFactoryBean and inject it with your message source (if any). Then you inject that basic validator in you custom validator and delegate annotation validation to it.
Using java configuration it could look like :
#Configuration
public class ValidatorConfig {
#Autowired
private MessageSource messageSource;
#Bean
public Validator basicValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource);
return validator;
}
}
Then you modify UserValidator to use it :
#Component
public class UserValidator implements Validator
{
#Autowired
#Qualifier("basicValidator")
private Validator basicValidator;
#Autowired
private UserDAO userDAO;
// ...
#Override
public void validate(Object target, Errors errors)
{
basicValidator.validate(target, errors);
// eventually stop if any errors
// if (errors.hasErrors()) { return; }
User user = (User) target;
if (userDAO.getUserByLogin(user.getLogin()) != null) {
errors.rejectValue("login", "NonUniq.user");
}
}
}
Well for me you have to delete the
#InitBinder
protected void initBinder(final WebDataBinder binder)
{
binder.setValidator(validator);
}
Leave the
#Valid #ModelAttribute("user") final User user,
BindingResult result
And after in the function make
validator.validate(user,result)
This way you will use the validation basic with the #Valid and after you will put make the more complex validation.
Because with the initBinder you are setting the validation with your complex logic and putting a way the basic logic.
Maybe is wrong, i use always the #Valid without any validator.

Spring won't inject a JPARepository bean

Dao
#Repository
public interface LoginDao extends JpaRepository<Login, Integer> {
Login findByLogin(String login);
}
Validator
#Component
public class PasswordChangeValidator implements Validator {
private LoginDao loginDao;
#Override
public boolean supports(Class<?> aClass) {
return PasswordChange.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
PasswordChange passwordChange = (PasswordChange) o;
**// There is a null pointer here because loginDao is null**
Login login = loginDao.findByLogin(passwordChange.getLoginKey());
}
public LoginDao getLoginDao() {
return loginDao;
}
#Autowired
public void setLoginDao(LoginDao loginDao) {
**// There is a debug point on the next line and it's hit on server startup and I can
// see the parameter us non-null**
this.loginDao = loginDao;
}
}
Controller
#Controller
#RequestMapping("api")
public class PasswordController {
#Autowired
PasswordService passwordService;
#InitBinder("passwordChange")
public void initBinder(WebDataBinder webDataBinder, WebRequest webRequest) {
webDataBinder.setValidator(new PasswordChangeValidator());
}
#RequestMapping(value = "/passwordChange", method = RequestMethod.POST)
public #ResponseBody PasswordInfo passwordInfo(#RequestBody #Valid PasswordChange passwordChange)
throws PasswordChangeException {
return passwordService.changePassword(passwordChange.getLoginKey(), passwordChange.getOldPassword(), passwordChange.getNewPassword());
}
}
I have the Dao listed above. This same dao bean gets injected in an #Service annotated class but not in #Component annotated Validator class. Well, not exactly the upon server startup I can see that the setter method gets called, but when I try to use this variable in a method the variable shows as null.
Does anybody see a problem with my configuration ? Please note that the loginDao bean gets injected into a service class, so the Context configuration is good.
Well there's your problem
webDataBinder.setValidator(new PasswordChangeValidator());
Spring can only manage beans it created. Here, you're creating the instance. Instead inject your bean into the #Controller and use it.
#Inject
private PasswordChangeValidator passwordChangeValidator;
...
webDataBinder.setValidator(passwordChangeValidator);

How to use session scoped bean properly

There are 3 classes in my Spring MVC app: a UserDetailsInterceptor class, an MyAdvice class and a UserDetails class (session scoped).
What I want to accomplish is simple:
UserDetailsInterceptor intercepts requests and set user's id in a session scoped UserDetails bean.
Later on, when the method in AOP advice class is called, retrieve user's id from the session scoped UserDetails bean.
Problem (also marked in the code below):
UserDetails object is null in MyAdvice class.
In UserDetailsInterceptor, userDetails.setUserID(request.getRemoteUser()); does nothing.
Code:
UserDetailsInterceptor class:
public class UserDetailsInterceptor extends HandlerInterceptorAdapter {
#Autowired
private UserDetails userDetails;
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//set user ID, but next line doesn't do anything for some reason (e.g. `userID` is still null)
userDetails.setUserID(request.getRemoteUser());
return true;
}
}
MyAdvice class:
public class MyAdvice implements MethodInterceptor {
#Autowired
private UserDetails userDetails; //It's null
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
//Print user ID
System.out.println(userDetails.getID());
return invocation.proceed();
}
}
UserDetails class:
public class UserDetails {
private String userID;
public void setUserID(String userID) {
this.userID= userID;
}
public String getUserID() {
return this.userID;
}
}
In dispatcher-servlet.xml:
<bean id="userDetails " class="package.UserDetails " scope="session">
<aop:scoped-proxy/>
</bean>
MyPointcutAdvisor class:
public class MyPointcutAdvisor implements PointcutAdvisor {
private MyPointcut pointcut = new MyPointcut();
private MyAdvice advice = new MyAdvice();
#Override
public Pointcut getPointcut() {
return this.pointcut;
}
#Override
public Advice getAdvice() {
return this.advice;
}
#Override
public boolean isPerInstance() {
return false;
}
}
Any ideas please? Thanks in advance.
Update:
By registering MyAdvice class, userDetails object in it is no longer null. However it is not the same object as the one in UserDetailsInterceptor. So the bean is not actually "session scoped"?
Answer:
The problem lies in following code:
private MyPointcut pointcut = new MyPointcut();
private MyAdvice advice = new MyAdvice();
Neither of them are managed by spring. As a result, things are being wired and not working the way we expected.
This
UserDetails object is null in MyAdvice class.
is not possible if the MyAdvice instance is managed by Spring. You must be instantiating it yourself instead of getting it from the context.
If Spring doesn't manage the object, it can't inject anything into #Autowired targets, so your field remains null.
If Spring was managing your object, a bean, and couldn't resolve the dependency, it would throw exceptions.

Store bean instance in Spring

I have an authenticating method in my web application, which gets a http parameter from another application. I load my user from the database and store it in a spring bean. In my login controller it is instantiated, but when I inject it in another controller, it losts its properties and I get 'null' when I want to reach my user. How can I make a bean's property available in all other controller? I don't want to use static properties... I tried to make the bean 'session scoped', but it doesn't work.
The suerBean:
#Service
#SessionScoped
public class SessionUserBean {
public Dolgozo user;
public Boolean userIsDolgozo;
public Boolean userIsIranyito;
public Boolean userIsVezeto;
public Boolean userIsOsztalyVezeto;
public void setUser(Dolgozo user) {
this.user = user;
}
public Dolgozo getUser() {
return user;
}
}
I set the value of the user property:
#Autowired
private SessionUser sessionUser;
#Autowired
private SessionUserBean user;
#Autowired
private HttpServletRequest request;
#RequestMapping("index.htm")
public String doLogin(#RequestParam String token) {
if (login.isUserAuthanticated(token)) {
user.setUser(sessionUser.getDolgozo());
return "sikeresBelepes";
}
return "sikertelenBelepes";
}
And this is where I want to use it:
#Autowired
private SessionUserBean user;
public void setUp() {
employees = drp.findByCsoportID(user.getUser().getCsoportid().getId());
}
What a mess!
Why not use Spring Security? It automatically manages Security Context for you by setting up a set of filters. And you have access to user information in any place of your app.
If no, you must set up your user in session somewhere in your code, you cannot just autowire it. Like this:
#RequestMapping("index.htm")
public String doLogin(#RequestParam String token) {
if (login.isUserAuthanticated(token)) {
user.setUser(sessionUser.getDolgozo());
return "sikeresBelepes";
session.addAttribute("securityUser", user);
}
return "sikertelenBelepes";
}
and then get it from session with getAttribute()

Categories

Resources