I'm using Spring 3.2.0. I have registered a few custom property editors for some basic needs as follows.
import editors.DateTimeEditor;
import editors.StrictNumberFormatEditor;
import java.math.RoundingMode;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import org.joda.time.DateTime;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.beans.propertyeditors.URLEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.context.request.WebRequest;
#ControllerAdvice
public final class GlobalDataBinder
{
#InitBinder
public void initBinder(WebDataBinder binder, WebRequest request)
{
binder.setIgnoreInvalidFields(true);
binder.setIgnoreUnknownFields(true);
//binder.setAllowedFields(someArray);
NumberFormat numberFormat=DecimalFormat.getInstance();
numberFormat.setGroupingUsed(false);
numberFormat.setMaximumFractionDigits(2);
numberFormat.setRoundingMode(RoundingMode.HALF_UP);
binder.registerCustomEditor(DateTime.class, new DateTimeEditor("MM/dd/yyyy HH:mm:ss", true));
binder.registerCustomEditor(Double.class, new StrictNumberFormatEditor(Double.class, numberFormat, true));
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
binder.registerCustomEditor(URL.class, new URLEditor());
}
}
I have this many editors registered so far. Two of them DateTimeEditor and StrictNumberFormatEditor have been customized by overriding respective methods to fulfill custom needs of number format and Joda-Time.
Since I'm using Spring 3.2.0, I can take advantage of #ControllerAdvice.
Spring recommends to list a set of allowed fields with the setAllowedFields() method so that malicious users can not inject values into bound objects.
From the docs about DataBinder
Binder that allows for setting property values onto a target object,
including support for validation and binding result analysis. The
binding process can be customized through specifying allowed fields,
required fields, custom editors, etc.
Note that there are potential security implications in failing to set
an array of allowed fields. In the case of HTTP form POST data for
example, malicious clients can attempt to subvert an application by
supplying values for fields or properties that do not exist on the
form. In some cases this could lead to illegal data being set on
command objects or their nested objects. For this reason, it is highly
recommended to specify the allowedFields property on the DataBinder.
I have a big application and obviously there are thousands of fields. Specifying and listing all of them with the setAllowedFields() is a tedious job. Additionally, somehow I need to remember them.
Changing a web page to remove some fields or add additional fields as the need arises again requires to modify the parameter value of the setAllowedFields() method to reflect those changes.
Is there any alternative to this?
Instead of using setAllowedFields() to white-list, you can use setDisallowedFields() to black-list. For example, from the petclinic sample application:
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
From a pure security standpoint white-listing is preferred to black-listing, but it maybe help ease the burden some.
setAllowedFields() is very handy when using entity objects directly in web layer. Alternatively, one could use dedicated data transfer objects (DTO), from which entity objects are constructed in the service layer. Not only can the factories be re-used, but also used outside the web context, e.g. for asynchronous messages. Besides, DTO inheritance doesn't have to follow entity inheritance, so you are free to design the DTO hierarchy according to the needs of the use-cases.
from http://static.springsource.org/spring-webflow/docs/2.0.x/reference/htmlsingle/spring-webflow-reference.html#view-model
4.9. Specifying bindings explicitly
Use the binder element to configure the exact set of model bindings usable by the view. This is particularly useful in a Spring MVC environment for restricting the set of "allowed fields" per view.
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="creditCard" />
<binding property="creditCardName" />
<binding property="creditCardExpiryMonth" />
<binding property="creditCardExpiryYear" />
</binder>
<transition on="proceed" to="reviewBooking" />
<transition on="cancel" to="cancel" bind="false" />
</view-state>
If the binder element is not specified, all public properties of the model are eligible for binding by the view. With the binder element specified, only the explicitly configured bindings are allowed.
Each binding may also apply a converter to format the model property value for display in a custom manner. If no converter is specified, the default converter for the model property's type will be used.
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="checkinDate" converter="shortDate" />
<binding property="checkoutDate" converter="shortDate" />
<binding property="creditCard" />
<binding property="creditCardName" />
<binding property="creditCardExpiryMonth" />
<binding property="creditCardExpiryYear" />
</binder>
<transition on="proceed" to="reviewBooking" />
<transition on="cancel" to="cancel" bind="false" />
</view-state>
In the example above, the shortDate converter is bound to the checkinDate and checkoutDate properties. Custom converters may be registered with the application's ConversionService.
Each binding may also apply a required check that will generate a validation error if the user provided value is null on form postback:
<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="checkinDate" converter="shortDate" required="true" />
<binding property="checkoutDate" converter="shortDate" required="true" />
<binding property="creditCard" required="true" />
<binding property="creditCardName" required="true" />
<binding property="creditCardExpiryMonth" required="true" />
<binding property="creditCardExpiryYear" required="true" />
</binder>
<transition on="proceed" to="reviewBooking">
<transition on="cancel" to="bookingCancelled" bind="false" />
</view-state>
In the example above, all of the bindings are required. If one or more blank input values are bound, validation errors will be generated and the view will re-render with those errors.
A solution to use binder with DTO (companydata in example) in case most of the form input values should be converted to null if empty, but there is a need to add few exceptions (setDisallowedFields didn't work for me).
#InitBinder()
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
}
#InitBinder
protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) {
binder.registerCustomEditor(String.class, "companydata.companyName", new StringTrimmerEditor(false));
binder.registerCustomEditor(String.class, "companydata.companyNumber", new StringTrimmerEditor(false));
}
Related
in seam faces is option of a validation of a whole form.
I can import:
xmlns:s="http://jboss.org/seam/faces"
and use:
<s:validateForm validatorId="oneOrMorePrimaryIndicesValidator"/>
Now we have to use omnifaces instead of seamfaces.
Is there any equivalent in omnifaces that do similar job?
Depends on the concrete functional requirement which isn't entirely clear from the question.
If you want to validate if one or more fields are filled out, use existing <o:validateOneOrMore>:
<o:validateOneOrMore components="foo bar baz" />
<h:inputText id="foo" />
<h:inputText id="bar" />
<h:inputText id="baz" />
Or, if you want to implement a custom validator for multiple fields, use <o:validateMultiple>:
<o:validateMultiple components="foo bar baz" validator="#{oneOrMorePrimaryIndicesValidator}" />
<h:inputText id="foo" />
<h:inputText id="bar" />
<h:inputText id="baz" />
Whereby #{oneOrMorePrimaryIndicesValidator} refers a managed bean (can be either JSF or CDI) which implements MultiFieldValidator:
#Named
#RequestScoped // Can also be #ApplicationScoped, depending on if validator should hold state.
public class OneOrMorePrimaryIndicesValidator implements MultiFieldValidator {
#Override
public boolean validateValues(FacesContext context, List<UIInput> components, List<Object> values) {
// ...
}
}
It should return false if values are invalid, otherwise true.
Either way, general usage instructions on e.g. components, message, invalidateAll and showMessageFor attributes can be found in ValidateMultipleFields javadoc.
A completely different alternative is to use <o:validateBean> with JSR303 bean validation groups to validate an entity at class level with a JSR303 ConstraintValidator<SomeGroupAnnotation, Bean>. Such a validator is not only useable in JSF (the web tier), but also in e.g. JPA (the data tier). E.g.
<h:inputText value="#{bean.entity.foo}" />
<h:inputText value="#{bean.entity.bar}" />
<h:inputText value="#{bean.entity.baz}" />
<o:validateBean value="#{bean.entity}" validationGroups="com.example.SomeGroup" />
A more concrete example is hard to give without having concrete code of current model and validator at hands.
Every documentation and examples that I read to be about validation form fields but I wish to show messages for others cases, for example to warn the view or flow have changed. So, that messages must be added on action methods.
In my case the app send to users a confirmation email, on flow there is a decision-state which redirect to views depending on link got declared a request parameter or not. Then I define a action-state which evaluate a method to confirm the email or warning to user about account's state (deleted or already enabled)
<on-start>
<set name="requestScope.code" value="requestParameters.code" />
<set name="requestScope.ln" value="requestParameters.ln" />
</on-start>
<decision-state id="checkConfirmation">
<if test="requestScope.code==null" then="login" else="confirmation" />
</decision-state>
<action-state id="confirmation">
<evaluate expression="login.confirmation(requestScope.ln,requestScope.code)" />
<transition on="yes" to="confirmationOk" />
<transition on="noUserFound" to="noUserFound" />
<transition on="userEnabled" to="userEnabled" />
<transition on="error" to="error" />
</action-state>
<view-state id="userEnabled" view="confirmation.xhtml">
<on-entry>
<set name="viewScope.operation" value="'enabled'" />
</on-entry>
<transition on="login" to="login" />
</view-state>
confirmation method
public String confirmation(String language,String emailCode){
logger.entry("Login.confirmation()");
String emailDecode=new String(Base64.decode(emailCode.getBytes()));
User user=userBo.getDao().findNamedQueryUnique("getUserByEmail",emailDecode);
if(user!=null){
if(!user.isEnabled()){
user.setEnabled(true);
try{
userBo.getDao().merge(user);
if(!EmailProvider.sendEmailEnabled(language,emailDecode)){
return "error";
}
}catch(DataAccessException e){
e.printStackTrace();
FlashMessages.addMessage(language,"es.project.properties.message.msg","unexpected.exception");
return "error";
}
}else{
// JSF way - FlashMessages.addMessage(language,"es.project.properties.message.msg","user.enable.confirmation");
//Spring webflow way - MessageContext context=new DefaultMessageContext();
//MessageBuilder builder=new MessageBuilder();
//context.addMessage(builder.code("user.enable.confirmation").build());
**********************************************************
*** I wish to add messages here and show in next view ****
**********************************************************
return "userEnabled";
}
}else{
//FlashMessages.addMessage(language,"es.project.properties.message.msg","user.validation.norfound.exception");
return "noUserFound";
}
return "yes";
}
I tried to follow spring webflow examples (booking-faces and booking-mvc) adding messages.properties on flow folder or creating a MessageSource bean, but I'm really lost on this matter.
I believe you are not having the messages.properties in classpath properly.
Suppose your file is in folder structure as:
src
|
|-resources
|
|-messages.properties
i.e make sure its in classpath.
You need to declare the ResourceBundleMessageSource with properties file in your servlet context xml file as:
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages" />
</bean>
Then you can access the properties as:
<action-state id="confirmation">
...
<transition on="userEnabled" to="userEnabled">
<!--This is the property you want to set in else part of confirmation method - instead include here-->
<set name="flashScope.flashMessageKey" value="'es.project.properties.message.msg'" />
</transition>
</action-state>
Include this in view:
<%# taglib prefix="fmt" uri="http://java.sun.com/jstl/fmt" %>
Then you can view this message on view render by:
<fmt:message key="${flashMessageKey}"/>
I found a solution for Spring,JSF 2.2 and i18 in this post. Basically, you should define a spring message source and create a spring component which will be called for jsf outputtext tag. Then how #Prasad wrote it's necessary to set a key in a spring flow or programatically.
Properties resources are src/main/resources/error_en.properties and src/main/resources/error_es.properties
MessageSource Bean
#Bean
public ReloadableResourceBundleMessageSource messageSource(){
ReloadableResourceBundleMessageSource msg=new ReloadableResourceBundleMessageSource();
msg.setBasename("classpath*:error");
return msg;
}
Spring component
#Component(value="message")
public class MessageSourceProvider extends HashMap {
private static final long serialVersionUID = 1L;
#Autowired
private MessageSource messageSource;
#Override
public String get(Object key) {
ServletRequest request = (ServletRequest) FacesContextWrapper.getCurrentInstance().getExternalContext().getRequest();
String message;
try {
message = messageSource.getMessage((String) key, null, request.getLocale());
}
catch (NoSuchMessageException e) {
message = "???" + key + "???";
}
return message;
}
}
JSF tag
<h:outputText value="#{message.unexpected}" />
I have a global-transition declared and I would like the value of the "validate" attribute to be conditionned or set in an action-state.
Here is how I would like to do it :
<decision-state id="decision_view">
<if test="condition == true" then="actionState1" />
</decision-state>
<action-state id="actionState1">
<evaluate result="flowScope.validateGT1" expression="true"/>
</action-state>
<global-transitions>
<transition on="gtransition1" to="gtransition1"
validate="flowScope.validateGT1" /> // Does not work, syntax error
</global-transitions>
This syntax does not work at all. Is there a way to determine the validate boolean dynamically ?
The project I am working on is using a 2.3.1 version of Spring webflow.
Thanks.
I think you cannot use the expression for boolean as it expects only literals true or false:
http://www.w3.org/TR/xmlschema-2/#boolean
Check the attirbute type for "validate" in spring webflow xsd:
xsd:attribute name="validate" type="xsd:boolean"
Instead what you can do is retrieve the boolean value in validator itself and decide if validation should be done or not as:
public class YourValidator {
public void validateStateId(YourModel model, ValidationContext context) {
RequestContext requestContext = RequestContextHolder.getRequestContext();
boolean shouldValidate = (Boolean)requestContext.getFlowScope.get("validateGT1");
if(shouldValidate){
MessageContext messages = context.getMessageContext();
...
}
}
}
Try to use evaluate
<evaluate expression="flowScope.validateGT1" result="flag" />
<global-transitions>
<transition on="gtransition1" to="gtransition1"
validate="flag" />
</global-transitions>
I have used jasig (3.5.1) cas sever and successfully configured.It works fine. my project have another requirement. I mentioned it below
i need another login mechanism. it mean rather than using stand username password, i need corporate code,mobile number and password authentication for corporate users. so i have created another Credential class for that
public class CodeMobileNumberCredintials implements Credentials{
#NotNull
#Size(min=1,message = "required.code")
private String code;
#NotNull
#Size(min=1, message = "required.mobileNumber")
private String mobileNumber;
#NotNull
#Size(min=1, message = "required.password")
private String password;
...
}
Then i created a variable called "codeMobileNumberCredintials"in web-flow.
<var name="credentials" class="org.jasig.cas.authentication.principal.UsernamePasswordCredentials" />
<var name="codeMobileNumberCredintials" class="org.jasig.cas.authentication.principal.CodeMobileNumberCredintials"/>
<view-state id="viewCorporateLoginForm" view="casCorporateLoginView" model="codeMobileNumberCredintials">
<binder>
<binding property="code" />
<binding property="mobileNumber" />
<binding property="password" />
</binder>
<on-entry>
<set name="viewScope.commandName" value="'codeMobileNumberCredintials'" />
</on-entry>
<transition on="submit" bind="true" validate="true" to="realCorporateSubmit">
<evaluate expression="authenticationViaFormAction.doCorporateBind(flowRequestContext, flowScope.codeMobileNumberCredintials)" />
</transition>
</view-state>
The issue is bean validation process not working for my custom login form.But normal username password form validated(when submiting form without givin username and password , It says "username and password blank"). But my custom autentication form not validated.It directly goes to controller class.
I have spent lot of time to do this. Can anyone help me to do my task.
Thank you
Amila
I had a similar problem. The easiest way to solve it, is to add new validator bean in
cas-servlet.xml Spring configuration file in cas-server-webapp module. In your case it will be:
<bean id="codeMobileNumberCredintialsValidator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
p:messageInterpolator-ref="messageInterpolator" />
There was an issue with spring web flow, resulting an error during validation. It occurs with stack trace as below and error page displayed, instead redisplaying login form:
2013-10-04 09:48:31,683 TRACE [org.jasig.cas.web.init.SafeDispatcherServlet] - <Entering method [service with arguments [[org.apache.catalina.connector.RequestFacade#411c345a, org.apache.catalina.connector.ResponseFacade#22b1221b]]>
2013-10-04 09:48:31,686 DEBUG [org.jasig.cas.web.FlowExecutionExceptionResolver] - <Ignoring the received exception due to a type mismatch> org.springframework.webflow.execution.repository.BadlyFormattedFlowExecutionKeyException: Badly formatted flow execution key '[Ljava.lang.String;#58714e00', the expected format is 'e<executionId>s<snapshotId>'
The fix is described here CAS-1142. It help me, and it summarize with adding one line <webflow:redirect-in-same-state value="false" /> to cas-servlet.xml resulting as below:
<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
<webflow:flow-execution-attributes>
<webflow:always-redirect-on-pause value="false"/>
<webflow:redirect-in-same-state value="false" />
</webflow:flow-execution-attributes>
<webflow:flow-execution-listeners>
<webflow:listener ref="terminateWebSessionListener" />
</webflow:flow-execution-listeners>
</webflow:flow-executor>
I am using spring webflow, this is my flow
<view-state id="welcome">
<transition on="emailEntered" to="checkEmail"></transition>
</view-state>
<decision-state id="checkEmail">
<if test="alta.checkEmail(requestParameters.email)"
then="okState"
else="errorState"/>
</decision-state>
<view-state id="okState"/>
<view-state id="errorState"/>
I have enabled auto-scanning in my servlet-context:
<context:component-scan base-package="com.me.myproj" />
I get a org.springframework.binding.expression.PropertyNotFoundException: Property not found error for state checkEmail. The problem is that it doesn't recognize my 'alta' bean, this is my Alta class (placed in com.me.myproj):
#Component
public class Alta {
public Alta(){
System.out.println("constructor ok");
}
public boolean checkEmail(String email){
return "my.name#email.com".equals(email);
}
}
If I explicitly create the bean:
<bean id="alta" class="com.me.myproj.Alta"/>
Then it works fine. So it seems that flow context doesn't recognize auto-scanned components, although alta is instanciated (as I saw when I debugged).
What can I do to avoid declaring explictly all beans involved in my flow?
Did you include
<context:annotation-config/>
in your servlet-context.xml?
When you explicitly create the bean in the XML you are naming the bean with name "alta" (id value). That is why you can execute methods from class Alta refering to "alta.checkEmail(...)".
<bean id="alta" class="com.me.myproj.Alta"/>
If you want to avoid XML declaration and use annotations only, you should specify that name in the annotation by just passing the name as argument [1]. For example:
#Component("alta")
public class Alta {
public Alta(){
System.out.println("constructor ok");
}
public boolean checkEmail(String email){
return "my.name#email.com".equals(email);
}
}
Hope this helps.
[1] http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/stereotype/Component.html