How to perform validation in spring mvc without using annotations - spring

#RequestMapping("/validateMsg")
public boolean validateEmp(#ModelAttribute Employee emp,BindingResult bindingResult,Model model){
boolean iserror=false;
if(emp.getFirstName()=="")
{
model.addAttribute("firstName","firstName is required");
iserror=true;
}
return iserror;
}
I have written this code is this correct

You can use a validator.
#Component
public class EmploeeValidator implements Validator{
#Override
public boolean supports(Class<?> clazz) {
return Emploee.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "someProp", "someProp.empty");
//other valdiation...
}
}
Then in the controller
#Autowired
private EmploeeValidator validator;
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(validator);
}
Use it:
#RequestMapping("/emploee")
public boolean addEmp(#Valid Employee emp,Errors errors){
if(errors.hasErrors()){
//it's not valid
} else {
//ok
}
}

Related

Custom method in PreAuthorize is not working "Failed to evaluate expression 'isAdmin()'"

#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Autowired
private ApplicationContext context;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(new UserPermissionEvaluator());
expressionHandler.setApplicationContext(context);
return expressionHandler;
}
}
And UserPermission class
#Component("UsrPermission")
public class UserPermissionEvaluator implements PermissionEvaluator {
#Override
public boolean hasPermission(Authentication authentication, Object targetObject, Object permission) {
if (!targetObject.toString().equals("true") && targetObject.toString().equals(permission.toString())) {
return true;
} else if (!targetObject.toString().equals("true")) {
return false;
}
...
return hasPermission;
}
public boolean isAdmin() {
return CustomSecurityPrincipal.getSecurityPrincipal().isAdmin();
}
#Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType,
Object permission) {
return false;
}
}
"hasPermission(x, y, z)" is working like charm. But, I tried to create new custom method, and since it is registerd in MethodSecurityConfig. I am trying to call it directly
#PreAuthorize("isAdmin()")
Error:-
org.springframework.expression.spel.SpelEvaluationException: EL1004E: Method call: Method isAdmin() cannot be found on type org.springframework.security.access.expression.method.MethodSecurityExpressionRoot
at org.springframework.expression.spel.ast.MethodReference.findAccessorForMethod(MethodReference.java:225) ~[spring-expression-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:134) ~[spring-expression-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:94) ~
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:114) ~[spring-expression-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:300) ~[spring-expression-5.1.6.RELEASE.jar:5.1.6.RELEASE]
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:26) ~[spring-security-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice.before(ExpressionBasedPreInvocationAdvice.java:59) ~[spring-security-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:72) ~[spring-security-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:40) ~[spring-security-core-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at
...
In order to create a new custom expression, you need to create a custom implementation of MethodSecurityExpressionOperations and add a new operation to it. Note that you can extend SecurityExpressionRoot to support default expressions:
public class CustomMethodSecurityExpressionRoot
extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
private Object filterObject;
private Object returnObject;
private Object target;
CustomMethodSecurityExpressionRoot(Authentication authentication) {
super(authentication);
}
#Override
public void setFilterObject(Object filterObject) {
this.filterObject = filterObject;
}
#Override
public Object getFilterObject() {
return filterObject;
}
#Override
public void setReturnObject(Object returnObject) {
this.returnObject = returnObject;
}
#Override
public Object getReturnObject() {
return returnObject;
}
void setThis(Object target) {
this.target = target;
}
#Override
public Object getThis() {
return target;
}
/**
* Custom 'isAdmin()' expression
*/
public boolean isAdmin() {
// TODO: Implement
return true;
}
}
Next, you need extend DefaultMethodSecurityExpressionHandler and make it to use CustomMethodSecurityExpressionRoot:
public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {
#Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) {
CustomMethodSecurityExpressionRoot root = new CustomMethodSecurityExpressionRoot(authentication);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(new AuthenticationTrustResolverImpl());
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
Finally, you should use CustomMethodSecurityExpressionHandler in the configuration:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new CustomMethodSecurityExpressionHandler();
}
}

Spring Configuration Properties Nested Custom Validation

I have nested properties class:
#ConfigurationProperties(prefix = "myapp", ignoreUnknownFields = false)
public class MyAppProperties implements Validator {
#Valid
private List<Server> servers = new ArrayList();
public MyAppProperties() {
}
public List<Server> getServers() {
return this.servers;
}
public void setServers(List<Server> servers) {
this.servers = servers;
}
#Override
public boolean supports(Class<?> clazz) {
return MyAppProperties.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
MyAppProperties properties = (MyAppProperties) target;
if (isEmpty(properties.getServers())) {
errors.rejectValue("servers", "myapp.servers", "Servers not provided");
}
}
public static class Server implements Validator {
private String name;
private String url;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
#Override
public boolean supports(Class<?> clazz) {
return Server.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Server server = (Server) target;
if (StringUtils.isBlank(server.getName())) {
errors.rejectValue("name", "server.name", "Server name not provided");
}
if (StringUtils.isBlank(server.getUrl())) {
errors.rejectValue("url", "server.url", "Server url not provided");
}
}
}
}
Now i want the validation to fire for both classes, but when i run with invalid values, the validation fires only for MyAppProperties but not for the list of Servers.
Am i doing something wrong, i'm not able to make this work. I want to use custom validator for both. Can anybody please help me make this work.
The invalid values that i pass are:
myapp.servers[0].name=
myapp.servers[0].url=
But this runs without any error

Issue in calling validator automic with #Valid in Spring controller

I am trying to call validator from controller using #Valid annotation, but control is not going to validator and proceeding without validating.
Controller
#Controller
#RequestMapping(value="/event")
public class EventController {
#Autowired
private EventService eventService;
#Autowired
EventValidator eventValidator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(eventValidator);
}
#RequestMapping(value="/add_event",method = RequestMethod.POST,produces=MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<AjaxJSONResponse> postAddEventForm(#Valid #RequestPart("event") Event event, MultipartHttpServletRequest request) {
Boolean inserted = eventService.addEvent(event);
String contextPath = request.getContextPath();
String redirectURL = StringUtils.isEmpty(contextPath)?"/event":contextPath+"/event";
return new ResponseEntity<AjaxJSONResponse>(new AjaxJSONResponse(inserted,"Event Added Successfully",redirectURL), HttpStatus.OK);
}
}
Validator
#Component
public class EventValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return Event.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
Event event = (Event)target;
if (event.getEventName() == null ||!StringUtils.hasText(event.getEventName())) {
errors.rejectValue("eventName", "", "Event Name is empty");
}
}
}
Please help on this.
Thank in advance

Spring Security: How do I enable custom expression result type support?

In my Spring Boot application I'm using the #PreAuthorize annotation in my controller methods to make them authorized. The expressions use simple boolean-returning methods, like this:
#ResponseStatus(OK)
#PreAuthorize("#auth.authentication.mayReadMe(principal)")
public UserDto readMe() {
...
The mayReadMe(...) method simply returns a boolean value, however it uses ternary logic under the hood and just converts a special enum to boolean:
boolean mayReadMe(#Nonnull UserDetails principal);
Now let's say I want to rework the authorization components and let the method return the enum:
#Nonnull
foo.bar.FooBarEnum mayReadMe(#Nonnull final UserDetails principal);
However, I'm getting the following exception:
java.lang.IllegalArgumentException: Failed to evaluate expression '#primaryAuth.authentication.mayReadMe(principal)'
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:15)
at org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice.before(ExpressionBasedPreInvocationAdvice.java:44)
at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:57)
at org.springframework.security.access.prepost.PreInvocationAuthorizationAdviceVoter.vote(PreInvocationAuthorizationAdviceVoter.java:25)
at org.springframework.security.access.vote.AffirmativeBased.decide(AffirmativeBased.java:62)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:232)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:64)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
...
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1001E:(pos 0): Type conversion problem, cannot convert from #javax.annotation.Nonnull foo.bar.FooBarEnum to java.lang.Boolean
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:78)
at org.springframework.expression.common.ExpressionUtils.convertTypedValue(ExpressionUtils.java:53)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:301)
at org.springframework.security.access.expression.ExpressionUtils.evaluateAsBoolean(ExpressionUtils.java:11)
... 113 common frames omitted
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [#javax.annotation.Nonnull foo.bar.FooBarEnum] to type [java.lang.Boolean]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:313)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195)
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:74)
... 116 common frames omitted
The exception message is really clear, but I can't inject my custom converter in any way. What I've tried so far:
Registering custom converters via WebMvcConfigurerAdapter.addFormatters(FormatterRegistry) (both Converter and GenericConverter)
Bean-ining a custom ExpressionBasedPreInvocationAdvice (but it shouldn't work as far as I understand)
... and a few other ways I can't recall after spending a few hours unfortunately.
How do I inject a custom type converter so the #PreAuthorization expressions could be aware of the foo.bar.FooBarEnum as the returning type?
Edit 1
Why do I need a custom type to be returned, and not a boolean. I'm also writing a simple REST API self-describing subsystem, just a simple GET /api endpoint to return a list of endpoints and so on. This list consists of a certain objects describing API end point, HTTP method, incoming and outgoing DTOs, and the last thing I'm trying to add to the definition object is an endpoint authorization policy expression. Note that it's not a good idea to return the #PreAuthorize string expression (I mean a raw string), but it might be good to return a custom object describing the authorization rules. What I want the most is returning an object like:
public final class AuthorizationExpression
implements BooleanSupplier {
...
public IExpression toExpression() {
...
}
where BooleanSupplier is expected to be used in the converter I'm trying to inject in order to satisfy the authorization needs -- just return true or false, and where IExpression is expected to be toString-ed in the GET /api handler using the Spring expression evaluator. Hence the mayReadMe signature might be as follows:
AuthorizationExpression mayReadMe(...)
so I could use AuthorizationExpression up to a certain use case. The FooBarEnum is just a simplification for the original question prior to the edit.
A suggestion, let your enum implement the conversion method:
public enum FooBarEnum {
// previous code
public boolean booleanValue() {
// TODO
}
}
And change your annotation:
#PreAuthorize("#auth.authentication.mayReadMe(principal).booleanValue()")
Figured it out. I only need to tune the DefaultMethodSecurityExpressionHandler instance. Let's assume the net two classes as library ones:
public abstract class CustomTypesGlobalMethodSecurityConfiguration
extends GlobalMethodSecurityConfiguration {
protected abstract ApplicationContext applicationContext();
protected abstract ConversionService conversionService();
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
final ApplicationContext applicationContext = applicationContext();
final TypeConverter typeConverter = new StandardTypeConverter(conversionService());
final DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler() {
#Override
public StandardEvaluationContext createEvaluationContextInternal(final Authentication authentication, final MethodInvocation methodInvocation) {
final StandardEvaluationContext decoratedStandardEvaluationContext = super.createEvaluationContextInternal(authentication, methodInvocation);
return new ForwardingStandardEvaluationContext() {
#Override
protected StandardEvaluationContext standardEvaluationContext() {
return decoratedStandardEvaluationContext;
}
#Override
public TypeConverter getTypeConverter() {
return typeConverter;
}
};
}
};
handler.setApplicationContext(applicationContext);
return handler;
}
}
where ForwardingStandardEvaluationContext is a simple forwarding decorator to decorate an instance of StandardEvaluationContext because the latter is ConversionService-aware:
public abstract class ForwardingStandardEvaluationContext
extends StandardEvaluationContext {
protected abstract StandardEvaluationContext standardEvaluationContext();
#Override public void setRootObject(final Object rootObject, final TypeDescriptor typeDescriptor) { standardEvaluationContext().setRootObject(rootObject, typeDescriptor); }
#Override public void setRootObject(final Object rootObject) { standardEvaluationContext().setRootObject(rootObject); }
#Override public TypedValue getRootObject() { return standardEvaluationContext().getRootObject(); }
#Override public void addConstructorResolver(final ConstructorResolver resolver) { standardEvaluationContext().addConstructorResolver(resolver); }
#Override public boolean removeConstructorResolver(final ConstructorResolver resolver) { return standardEvaluationContext().removeConstructorResolver(resolver); }
#Override public void setConstructorResolvers(final List<ConstructorResolver> constructorResolvers) { standardEvaluationContext().setConstructorResolvers(constructorResolvers); }
#Override public List<ConstructorResolver> getConstructorResolvers() { return standardEvaluationContext().getConstructorResolvers(); }
#Override public void addMethodResolver(final MethodResolver resolver) { standardEvaluationContext().addMethodResolver(resolver); }
#Override public boolean removeMethodResolver(final MethodResolver methodResolver) { return standardEvaluationContext().removeMethodResolver(methodResolver); }
#Override public void setMethodResolvers(final List<MethodResolver> methodResolvers) { standardEvaluationContext().setMethodResolvers(methodResolvers); }
#Override public List<MethodResolver> getMethodResolvers() { return standardEvaluationContext().getMethodResolvers(); }
#Override public void setBeanResolver(final BeanResolver beanResolver) { standardEvaluationContext().setBeanResolver(beanResolver); }
#Override public BeanResolver getBeanResolver() { return standardEvaluationContext().getBeanResolver(); }
#Override public void addPropertyAccessor(final PropertyAccessor accessor) { standardEvaluationContext().addPropertyAccessor(accessor); }
#Override public boolean removePropertyAccessor(final PropertyAccessor accessor) { return standardEvaluationContext().removePropertyAccessor(accessor); }
#Override public void setPropertyAccessors(final List<PropertyAccessor> propertyAccessors) { standardEvaluationContext().setPropertyAccessors(propertyAccessors); }
#Override public List<PropertyAccessor> getPropertyAccessors() { return standardEvaluationContext().getPropertyAccessors(); }
#Override public void setTypeLocator(final TypeLocator typeLocator) { standardEvaluationContext().setTypeLocator(typeLocator); }
#Override public TypeLocator getTypeLocator() { return standardEvaluationContext().getTypeLocator(); }
#Override public void setTypeConverter(final TypeConverter typeConverter) { standardEvaluationContext().setTypeConverter(typeConverter); }
#Override public TypeConverter getTypeConverter() { return standardEvaluationContext().getTypeConverter(); }
#Override public void setTypeComparator(final TypeComparator typeComparator) { standardEvaluationContext().setTypeComparator(typeComparator); }
#Override public TypeComparator getTypeComparator() { return standardEvaluationContext().getTypeComparator(); }
#Override public void setOperatorOverloader(final OperatorOverloader operatorOverloader) { standardEvaluationContext().setOperatorOverloader(operatorOverloader); }
#Override public OperatorOverloader getOperatorOverloader() { return standardEvaluationContext().getOperatorOverloader(); }
#Override public void setVariable(final String name, final Object value) { standardEvaluationContext().setVariable(name, value); }
#Override public void setVariables(final Map<String, Object> variables) { standardEvaluationContext().setVariables(variables); }
#Override public void registerFunction(final String name, final Method method) { standardEvaluationContext().registerFunction(name, method); }
#Override public Object lookupVariable(final String name) { return standardEvaluationContext().lookupVariable(name); }
#Override public void registerMethodFilter(final Class<?> type, final MethodFilter filter) throws IllegalStateException { standardEvaluationContext().registerMethodFilter(type, filter); }
}
And then a couple application classes:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = false)
class SecurityConfiguration
extends CustomTypesGlobalMethodSecurityConfiguration {
private final ApplicationContext applicationContext;
private final ConversionService conversionService;
public SecurityConfiguration(
#Autowired final ApplicationContext applicationContext,
#Autowired final ConversionService conversionService
) {
this.applicationContext = applicationContext;
this.conversionService = conversionService;
}
#Override
protected ApplicationContext applicationContext() {
return applicationContext;
}
#Override
protected ConversionService conversionService() {
return conversionService;
}
}
And finally the conversion service configuration:
#Configuration
class ConversionConfiguration {
#Bean
public ConversionService conversionService() {
final DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(FooBar.class, Boolean.class, FooBar::mayProceed);
return conversionService;
}
}
The code above makes #PreAuthorize to understand FooBar-returning expressions.

How to combine JSR-303 and Spring Validator class in a service layer?

I have some model class
public class Account {
#Email
private String email;
#NotNull
private String rule;
}
and spring-validator
public class AccountValidator implements Validator {
#Override
public boolean supports(Class aClass) {
return Account.class.equals(aClass);
}
#Override
public void validate(Object obj, Errors errors) {
Account account = (Account) obj;
ValidationUtils.rejectIfEmpty(errors, "email", "email.required");
ValidationUtils.rejectIfEmpty(errors, "rule", "rule.required");
complexValidateRule(account.getRule(), errors);
}
private void complexValidateRule(String rule, Errors errors) {
// ...
}
}
I run in my service
AccountValidator validator = new AccountValidator();
Errors errors = new BeanPropertyBindingResult(account, "account");
validator.validate(account, errors);
Can I add to my validation process constraints #Email, #NotNull (JSR-303) and don't describe these rules in AccountValidator?
I know how works #Valid in spring-controllers, but what's about service layer? Is it possible? How to do such kind of validation in a proper way? May I should use Hibernate Validator?
Spring provides an Adapter to merge both validation APIs.
See the current Spring JavaDoc for more information.
An possible implementation would be
public class AccountValidator implements Validator {
private final SpringValidatorAdapter validator;
public AccountValidator(SpringValidatorAdapter validator) {
super();
this.validator = validator;
}
#Override
public boolean supports(Class aClass) {
return Account.class.equals(aClass);
}
#Override
public void validate(Object obj, Errors errors) {
//jsr303
validator.validate(obj, errors);
//custom rules
Account account = (Account) obj;
complexValidateRule(account.getRule(), errors);
}
private void complexValidateRule(String rule, Errors errors) {
// ...
}
}

Resources