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()
Related
I have a DTO which is validated at Controller layer with a mix of BeanValidation (javax.validation) and a custom Validator (org.springframework.validation.Validator). This way I can check if the input provided is valid and then convert the DTO in an entity and forward it to the Service layer.
#Data
public class UserDTO {
#NotBlank
#Size(max = 25)
private String name;
#NotNull
private Date birthday;
#NotNull
private Date startDate;
private Date endDate;
private Long count;
}
public class UserDTOValidator implements Validator {
private static final String START_DATE= "startDate";
private static final String END_DATE= "endDate";
private static final String COUNT= "count";
#Override
public boolean supports(Class<?> clazz) {
return UserDTO.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
UserDTO vm = (UserDTO) target;
if (vm.getEndDate() != null) {
if (vm.getStartDate().after(vm.getEndDate())) {
errors.rejectValue(START_DATE, ErrorCode.ILLEGAL_ARGUMENT.toString(), ErrorCode.ILLEGAL_ARGUMENT.description());
}
if (vm.getEndDate().equals(vm.getStartDate()) || vm.getEndDate().before(vm.getStartDate())) {
errors.rejectValue(END_DATE, ErrorCode.ILLEGAL_ARGUMENT.toString(), ErrorCode.ILLEGAL_ARGUMENT.description());
}
}
if (vm.getCount() < 1) {
errors.rejectValue(COUNT, ErrorCode.ILLEGAL_ARGUMENT.toString(), ErrorCode.ILLEGAL_ARGUMENT.description());
}
.....
}
}
public class UserController {
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new UserDTOValidator());
}
#PostMapping()
public ResponseEntity<UserDTO> create(#RequestBody #Valid UserDTO userDTO) {
.....
}
.....
}
Then there is the business logic validation. For example: the #Entity User's startDate must be after some event occurred and the count has to be greater than some X if the last created User's birthDay is in Summer, in other case, the entity should be discarded by the User service.
#Service
#Transactional
public class UserServiceImpl implements UserService {
#Autowired
private UserRepository userRepository;
#Autowired
private SomeEventService someEventService ;
#Override
public User create(User entity) {
String error = this.validateUser(entity);
if (StringUtils.isNotBlank(error)) {
throw new ValidationException(error);
}
return this.userRepository.save(entity);
}
....
private String validateUser(User entity) {
SomeEvent someEvent = this.someEventService.get(entity.getName());
if (entity.getStartDate().before(someEvent.getDate())) {
return "startDate";
}
User lastUser = this.userRepository.findLast();
....
}
}
However I feel like this is not the best approach to handle business logic validation. What should I do? ConstraintValidator/HibernateValidator/JPA Event listeners? Can they work at #Entity class level or I have to create X of them for each different field check? How do you guys do it in a real production application?
In my suggestion,
Use classic field level validation by #Valid
sample
void myservicemethod(#Valid UserDTO user)
For custom business level validation in entity level, create validate method in DTO itself
sample
class UserDTO {
//fields and getter setter
void validate() throws ValidationException {
//your entity level business logic
}
}
This strategy will help to keep entity specific validation logic will be kept within the entity
If still you have validation logic that requires some other service call, then create custom validation annotation with custom ConstraintValidator (eg. question on stackoverflow). In this case, my preference will be to invoke UserDTO.validate() from this custom validator in spiote of calling from service
This will help to keep your validation logic separated from service layer and also portable and modular
I want to use user-information after login succeed. I thought about storing it into session attribute. or using #scope('session) annotation. but I haven't found the best way of doing it. so I just stored it into model-attribute.
#Controller
#RequestMapping(value = "/user")
public class UserController {
#Autowired
private UserService userService;
#Autowired
private UserProfileService userProfileService;
#ModelAttribute("user")
public User getUserModel () {
return userService.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName());
}
#ModelAttribute("userProfile")
public UserProfile getUserProfile() {
return userProfileService.findByUser(userService.findByEmail(SecurityContextHolder.getContext().getAuthentication().getName()));
}
#GetMapping("/")
public String userIndex() {
logger.info("UserIndex");
return "userPage";
}
As you can see, SecurityContextHolder.getContext().getAuthentication().getName() --> this method repeated. every time user make HTTP request, is this good practice? or any better way of store user-infomation in application?
I would go with this.
#RequestMapping(value = "/username", method = RequestMethod.GET)
#ResponseBody
public String currentUserName(Authentication authentication) {
return authentication.getName();
}
I want to pass the user object I use for authentication in a filter to the resource. Is it possible?
I'm using wildfly 10 (resteasy 3)
#Secured
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
#Inject
private UserDao userDao;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
logger.warn("Filter");
String uid = requestContext.getHeaderString("Authorization");
User user;
if((user = validateUser(uid)) == null) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
}
private User validateUser(String uid) {
return userDao.getById(uid);
}
}
There are two ways I could see to do this. The first is, perhaps, the more standard way but is also more code. Ultimately you'll inject the user as part of the request. However, the first thing you need for this solution is a Principal. A very simple one might be:
import java.security.Principal;
...
public class UserPrinicipal implements Prinicipal {
// most of your existing User class but needs to override getName()
}
Then, in your filter:
...
User user;
if((user = validateUser(uid)) == null) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return user;
}
#Override
public boolean isUserInRole(String role) {
// whatever works here for your environment
}
#Override
public boolean isSecure() {
return containerRequestContext.getUriInfo().getAbsolutePath().toString().startsWith("https");
}
#Override
public String getAuthenticationScheme() {
// again, whatever works
}
});
In the class where you want the User, you could do something like:
#Path("/myservice")
public class MyService {
#Context
private SecurityContext securityContext;
#Path("/something")
#GET
public Response getSomething() {
User user = (User)securityContext.getUserPrincipal();
}
}
I've implemented it this way and it works pretty well. However, an arguably simpler way is to just store the user in the session:
#Context
private HttpServletRequest request;
...
User user;
if((user = validateUser(uid)) == null) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
request.getSession().setAttribute("user", user);
Then, in your service:
#Path("/myservice")
public class MyService {
#Context
private SecurityContext securityContext;
#Path("/something")
#GET
public Response getSomething(#Context HttpServletRequest request) {
User user = (User)request.getSession().getAttribute("user");
}
}
The downside of the second method is that you are really no longer a stateless service as you're storing state somewhere. But the HttpSession is there even if you don't use it.
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);
}
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.