I'm making a web file browser using ZK Components and find a block. Is there any way to update the ListBox model using the selected item of the listbox?
The use case is when traversing the files and folder, the user click the folder, and the list is refreshed with the content of the selected folder. The selection event is triggered and for regular file, it handle well, but not the folder.
My Code:
myfilesvm.zul
<zk>
<window apply="org.zkoss.bind.BindComposer"
viewModel="#id('vm') #init('com.my.zk.mvvm.MyFilesViewModel')">
<hlayout>
<listbox vflex="true" hflex="1" model="#load(vm.files)"
id="fileBrowser" selectedItem="#bind(vm.selectedFile)">
<auxhead>
<auxheader colspan="3">File List</auxheader>
<auxheader colspan="3">
<hlayout>
<!-- breadcrumb, implemented later -->
</hlayout>
</auxheader>
</auxhead>
<listhead>
<listheader label="Name" />
<listheader label="Size" />
<listheader label="Modified" />
</listhead>
<template name="model" var="file">
<listitem>
<listcell label="#load(file.name)" />
<listcell label="#load(file.length())" />
<listcell label="#load(file.lastModified())" />
</listitem>
</template>
</listbox>
</hlayout>
<separator />
</window>
</zk>
MyFilesViewModel.java
package com.my.zk.mvvm;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zul.Filedownload;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;
import com.my.zk.FileCrumbManager;
public class MyFilesViewModel {
private static Logger log = LoggerFactory.getLogger(MyFilesViewModel.class);
// AuthenticationService authService = new AuthenticationServiceImpl();
// UserCredential cre = authService.getUserCredential();
String homeFolder = "D:\\path\\home";
ListModel<File> files = new ListModelList<File>(Arrays.asList(FileCrumbManager.populateList(new File(homeFolder))));
File selectedFile;
#Init
public void init() { // Initialize
}
public ListModel<File> getFiles() {
return files;
}
#NotifyChange({ "selectedFile" })
public void setFiles(ListModel<File> files) {
this.files = files;
}
public File getSelectedFile() {
return selectedFile;
}
public void pilihFile() {
if (getSelectedFile().isDirectory()) {
log.info("File is a directory");
this.files = new ListModelList<File>(
Arrays.asList(FileCrumbManager.populateList(new File(getSelectedFile().getAbsolutePath()))));
} else {
try {
Filedownload.save(getSelectedFile(), null);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
log.error(e.getMessage());
}
}
}
public void setSelectedFile(File selectedFile) {
this.selectedFile = selectedFile;
pilihFile();
}
}
Appreciate for any help. Thank you.
The correct way to refresh ListModelList in pilihFile() is:
this.files.clear();
this.files.addAll(Arrays.asList(FileCrumbManager.populateList(new File(getSelectedFile().getAbsolutePath()))));
Because Listbox is model-driven rendering, you should control the rendering by manipulating the model object. When you call methods of ListModelList, it will notify Listbox to render into a browser. If you replace this.files with a new object, ZK doesn't know it. That's why your browser doesn't have the update.
I am trying to recreate the spring-boot angularjs example application from here.
When I run the application using ./mnvw spring-boot:run the following error shows:
[ERROR] ERROR in src/app/app.component.html(6,18): : Property 'id' does not exist on type '{}'.
[ERROR] src/app/app.component.html(7,23): : Property 'content' does not exist on type '{}'.
My source code is the same as the link provided above but for clarity, the typescript file:
import { Component } from '#angular/core';
import {HttpClient} from '#angular/common/http';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Demo';
data = {};
constructor(private http: HttpClient) {
http.get('resource').subscribe(data => this.data = data);
}
}
The html file:
<div style="text-align:center"class="container">
<h1>
Welcome {{title}}!
</h1>
<div class="container">
<p>Id: <span>{{data.id}}</span></p>
<p>Message: <span>{{data.content}}</span></p>
</div>
</div>
And the java controller:
#SpringBootApplication
#Controller
public class AngularApplication {
public static void main(String[] args) {
SpringApplication.run(AngularApplication.class, args);
}
#GetMapping("/resource")
#ResponseBody
public Map<String, Object> home() {
Map<String, Object> model = new HashMap<String, Object>();
model.put("id", UUID.randomUUID().toString());
model.put("content", "Hello World");
return model;
}
}
The app.module.ts file imports HttpClientModule. When I ng serve the application to localhost:4200 the site loads but without data.
The idea is to service the /resource request and return an object with the right keys for the client, but the keys do not exist and/or are not being recognized by the client.
How do I correctly pass the generated id and hello world content to the data object?
There was a tiny difference in package.json that was causing the issue. The one from the provided link uses "build": "ng build" and the default generated one from CLI uses "build": "ng build --prod". After changing this the example is working.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
In a Spring MVC app using Spring Security, I want to use a custom AuthenticationProvider to check n-number of additional fields beyond the default username and password. For example, if a user wants to authenticate, I want her to have to supplement her username and password with a pin code she receives via email, a pincode she receives via text, and n number of other credentials. However, to keep this question narrow, let's just focus on adding one additional pin to the login, but let's set it up in a way that enables us to add n-other credentials easily afterwards.
I want to use Java configuration.
I have created a custom AuthenticationProvider, a custom AuthenticationFilter, custom UserDetailsService, and a few other changes.
But the app is granting access when a user tries to log in whether or not the user has valid credentials, as shown in a screen shot in the instructions for reproducing the problem below. What specific changes need to be made to the code that I am sharing so that the custom n-factor authentication can function properly?
The structure of my test project is shown in the following screen shots:
Here is the Java code structure in eclipse project explorer:
{ Image host not available }
The XML config files can be located by scrolling down in project explorer to show the following:
{ Image host not available }
The view code can be found by scrolling a little further down in project explorer as follows:
{ Image host not available }
You can download and explore all this code in a working Eclipse project:
{ File now deleted }
CustomAuthenticationProvider.java is:
package my.app.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
public class CustomAuthenticationProvider implements AuthenticationProvider{
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
List<GrantedAuthority> grantedAuths = new ArrayList<>();
if (name.equals("admin") && password.equals("system")) {
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
if(pincodeEntered(name)){
grantedAuths.add(new SimpleGrantedAuthority("registered"));
}
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
private boolean pincodeEntered(String userName){
// do your check here
return true;
}
}
MessageSecurityWebApplicationInitializer.java is:
package my.app.config;
import org.springframework.core.annotation.Order;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
#Order(2)
public class MessageSecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}
TwoFactorAuthenticationFilter.java is:
package my.app.config;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class TwoFactorAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{
private String extraParameter = "extra";
private String delimiter = ":";
/**
* Given an {#link HttpServletRequest}, this method extracts the username and the extra input
* values and returns a combined username string of those values separated by the delimiter
* string.
*
* #param request The {#link HttpServletRequest} containing the HTTP request variables from
* which the username client domain values can be extracted
*/
#Override
protected String obtainUsername(HttpServletRequest request){
String username = request.getParameter(getUsernameParameter());
String extraInput = request.getParameter(getExtraParameter());
String combinedUsername = username + getDelimiter() + extraInput;
System.out.println("Combined username = " + combinedUsername);
return combinedUsername;
}
/**
* #return The parameter name which will be used to obtain the extra input from the login request
*/
public String getExtraParameter(){
return this.extraParameter;
}
/**
* #param extraParameter The parameter name which will be used to obtain the extra input from the login request
*/
public void setExtraParameter(String extraParameter){
this.extraParameter = extraParameter;
}
/**
* #return The delimiter string used to separate the username and extra input values in the
* string returned by <code>obtainUsername()</code>
*/
public String getDelimiter(){
return this.delimiter;
}
/**
* #param delimiter The delimiter string used to separate the username and extra input values in the
* string returned by <code>obtainUsername()</code>
*/
public void setDelimiter(String delimiter){
this.delimiter = delimiter;
}
}
SecurityConfig.java is:
package my.app.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider());
}
#Bean
AuthenticationProvider customAuthenticationProvider() {
CustomAuthenticationProvider impl = new CustomAuthenticationProvider();
return impl ;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/secure-home")
.usernameParameter("j_username")
.passwordParameter("j_password")
.loginProcessingUrl("/j_spring_security_check")
.failureUrl("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.and()
.authorizeRequests()
.antMatchers("/secure-home").hasAuthority("registered")
.antMatchers("/j_spring_security_check").permitAll()
.and()
.userDetailsService(userDetailsService());
}
}
User.java is:
package my.app.model;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
#Entity
#Table(name="users")
public class User implements UserDetails{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
private Integer id;
#Column(name= "email", unique=true, nullable=false)
private String login;//must be a valid email address
#Column(name = "password")
private String password;
#Column(name = "phone")
private String phone;
#Column(name = "pin")
private String pin;
#Column(name = "sessionid")
private String sessionId;
#ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(name="user_roles",
joinColumns = {#JoinColumn(name="user_id", referencedColumnName="id")},
inverseJoinColumns = {#JoinColumn(name="role_id", referencedColumnName="id")}
)
private Set<Role> roles;
public Integer getId() {return id;}
public void setId(Integer id) { this.id = id;}
public String getPhone(){return phone;}
public void setPhone(String pn){phone = pn;}
public String getPin(){return pin;}
public void setPin(String pi){pin = pi;}
public String getSessionId(){return sessionId;}
public void setSessionId(String sd){sessionId = sd;}
public String getLogin() {
return login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
//roles methods
public void addRole(Role alg) {roles.add(alg);}
public Set<Role> getRoles(){
if(this.roles==null){this.roles = new HashSet<Role>();}
return this.roles;
}
public void setRoles(Set<Role> alg){this.roles = alg;}
public boolean isInRoles(int aid){
ArrayList<Role> mylgs = new ArrayList<Role>();
mylgs.addAll(this.roles);
for(int a=0;a<mylgs.size();a++){if(mylgs.get(a).getId()==aid){return true;}}
return false;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// TODO Auto-generated method stub
return null;
}
#Override
public String getUsername() {
// TODO Auto-generated method stub
return null;
}
#Override
public boolean isAccountNonExpired() {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean isAccountNonLocked() {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean isCredentialsNonExpired() {
// TODO Auto-generated method stub
return false;
}
#Override
public boolean isEnabled() {
// TODO Auto-generated method stub
return false;
}
}
The xml config is in business-config.xml and is:
<beans profile="default,spring-data-jpa">
<!-- lots of other stuff -->
<bean class="my.app.config.SecurityConfig"></bean>
</beans>
<!-- lots of unrelated stuff -->
In addition, mvc-core-config.xml contains the following:
<!-- lots of other stuff -->
<mvc:view-controller path="/" view-name="welcome" />
<mvc:view-controller path="/login" view-name="login" />
And login.jsp looks like this:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<%#taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%# page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Custom Login page</title>
<style>.error {color: red;}</style>
</head>
<body>
<div class="container">
<h1>Custom Login page</h1>
<p>
<c:if test="${error == true}">
<b class="error">Invalid login or password or pin.</b>
</c:if>
</p>
<form method="post" action="<c:url value='j_spring_security_check'/>" >
<table>
<tbody>
<tr>
<td>Login:</td>
<td><input type="text" name="j_username" id="j_username"size="30" maxlength="40" /></td>
</tr>
<tr>
<td>Password:</td>
<td><input type="password" name="j_password" id="j_password" size="30" maxlength="32" /></td>
</tr>
<tr>
<td>Pin:</td>
<td><input type="text" name="pin" id="pin"size="30" maxlength="40" /></td>
</tr>
<tr>
<td colspan=2>
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
</td>
</tr>
<tr>
<td></td>
<td><input type="submit" value="Login" /></td>
</tr>
</tbody>
</table>
</form>
</div>
</body>
</html>
The spring security dependencies in 'pom.xml' are:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>3.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>3.2.2.RELEASE</version>
</dependency>
Download and reproduce on your machine
I have also uploaded a working Eclipse project which contains the bare minimum code required to reproduce the problem on your local devbox. You can download the Eclipse project here:
{ File now deleted }
Once you have downloaded the zipped project, you can reproduce the problem on your machine by following these steps:
1.) Unzip the zip file to a new folder
2.) In Eclipse, do File > Import > Existing Maven Projects
3.) Click Next. Browse to folder of unzipped project. Complete wizard to import project.
4.) Right click on project name in eclipse and do Maven > Download sources
5.) Right click on project name again in eclipse and do Maven > Update project
6.) Open MySQL and create an empty new database called somedb
7.) In the Eclipse project, open data-access.properties as shown in the following picture, and change someusername and somepassword to your real username and password for your MySQL.
{ Image host not available }
8.) In Eclipse, right click the project and chose Run As .. Run on server.. . This should launch the app so that you see the following in your browser at the http://localhost:8080/n_factor_auth/ url:
{ Image host not available }
9.) Change the URL to http://localhost:8080/n_factor_auth/secure-home to see that you were redirected to http://localhost:8080/n_factor_auth/login which serves the sample custom login page, which requires a pin in addition to the username and password. Note that the result needs to accommodate n-factors and not simply adding a single pin code:
{ Image host not available }
10.) Insert test credentials into the MySQL database by running the following SQL commands, which you could put in a .sql file and run from the MySQL command line using the source command. Note that the database objects will be deleted and recreated empty every time the app starts because hbm2ddl is enabled to simplify this example. Thus, the following SQL commands will need to be re-run every time you reload the app in Eclipse.
SET FOREIGN_KEY_CHECKS=0;
INSERT INTO `roles` VALUES (100,'registered');
INSERT INTO `user_roles` VALUES (100,100);
INSERT INTO `users` (id, email,password, phone, pin) VALUES (100,'me#mydomain.com','somepassword','xxxxxxxxxx', 'yyyy');
SET FOREIGN_KEY_CHECKS=1;
11.) Try to login using any credentials (valid or invalid), and get the following successful login screen (Note that the user gets logged in whether or not they give valid credentials):
{ Image host not available }
That's it. You now have the problem recreated on your machine, including all the code shown above, but in a working minimalist eclipse project. So now how do you answer the OP above? What changes do you make to the code above, and what else do you do in order to get the custom authenticator to engage upon login?
I am interested to learn what specific changes need to be made to the minimalist download app in order to enable n-factor authentication. I will validate by checking your suggestions in the sample app on my machine.
Thanks to various people (including M.Deinum) who have suggested deleting redundant XML config to create the current version shown in this posting.
First, some explanation about the interfaces you are working with and the role they play in the authentication process:
Authentication - represents the result of authenticating a user. Holds the authorities granted to that user and any additional details that may be needed about the user. As there is no way for the framework to know, what details are going to be needed, the authentication object has a getDetails method that can return any object
AuthenticationProvider - object that can create an Authentication object in some way. To make them more reusable, some (or most) of AuthenticationProviders refrain from setting the user details on the Authentication object, as each application may need specific user details. Instead they delegate the process of resolving the user details to a settable UserDetailsService
UserDetailsService - a strategy for retrieving the user details required in your application.
So, if you are creating a custom AuthenticationProvider you may not even need to implement it in a way that requires a UserDetailsService. The decission is up to you and depends, on whether you plan on reusing your implementation in other projects.
As for the compilation problems in your code, you are mixing two ways of providing the UserDetailsService. In the CustomAuthenticationProvider you have annotated the userService field with the #Inject annotation .This means, that the container (Spring application context in your case) is to find a suitable implementation and inject it into that field at runtime using reflection. The process of setting this field by the context is called dependency injection. In the SecurityConfig class you are trying to provide the implementation yourself by setting the field through the setUserDetailsService method that does not exist in your class.
To resolve this problem you need to decide to use one of the ways to provide the UserDetails service and either:
remove the #Inject annotation and create the setUserDetailsService method, or
remove the line when you are calling the non-existant method and declare your implementation of the UserDetailsService as a bean
As for which of the ways should you choose, the dependecy injection way may by better if you can find a way of making your SecurityConfig class reusable in other projects. In that case you could just import it (by using the #Import annotaion) and declare a different UserDetailsSerice implementation as a bean in your next application and have it working.
Usually, classes like the SecurityConfig are not really reusable, so creating the setter and removing the dependency injection would probably be my first choice.
EDIT
A working, albeit a simplistic implementation (based heavily on this blog entry) would be:
public class CustomAuthenticationProvider implements AuthenticationProvider{
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
List<GrantedAuthority> grantedAuths = new ArrayList<>();
if (name.equals("admin") && password.equals("system")) {
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
}
if(pincodeEntered(name)){
grantedAuths.add(new SimpleGrantedAuthority("ROLE_PINCODE_USER"));
}
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
private boolean pincodeEntered(String userName){
// do your check here
return true;
}
}
Then in your config class change the following method:
#Bean
AuthenticationProvider customAuthenticationProvider() {
return new CustomAuthenticationProvider();
}
The first thing we need to do is extend the UsernamePasswordAuthenticationFilter class so that it can handle a second input field.
public class TwoFactorAuthenticationFilter extends UsernamePasswordAuthenticationFilter
{
private String extraParameter = "extra";
private String delimiter = ":";
//getters and setters
#Override
protected String obtainUsername(HttpServletRequest request)
{
String username = request.getParameter(getUsernameParameter());
String extraInput = request.getParameter(getExtraParameter());
String combinedUsername = username + getDelimiter() + extraInput;
return combinedUsername;
}
}
obtainUsername() This method is to retrieve the username and “extra” input field from the HttpServletRequest object that’s passed in.
It then concatenates these two values into one string, separating them by the delimiter string (a colon, by default).
It then returns this combined string. The parameter from which the “extra” input field is read is extra by default.
UserDetailsService should look like this:
#Override
public UserDetails loadUserByUsername(String input) throws UsernameNotFoundException, DataAccessException
{
String[] split = input.split(":");
if(split.length < 2)
{
throw new UsernameNotFoundException("Must specify both username and corporate domain");
}
String username = split[0];
String domain = split[1];
User user = userDao.findByUsernameAndDomain(username, domain);
if(user == null)
{
throw new UsernameNotFoundException("Invalid username or corporate domain");
}
return user;
}
Split the given username into its two components: the username and the extra field. In this example, the extra field is the user’s corporate domain.
Once we have the username and the domain, we can use our DAO to find the matching user.
Last Puzzle:
TwoFactorAuthenticationFilter:
<http use-expressions="true" auto-config="false" entry-point-ref="loginUrlAuthenticationEntryPoint">
<intercept-url pattern="/secured" access="isAuthenticated()" />
<intercept-url pattern="/**" access="permitAll" />
<custom-filter position="FORM_LOGIN_FILTER" ref="twoFactorAuthenticationFilter" />
<logout logout-url="/logout" />
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="authenticationProvider" />
</authentication-manager>
<beans:bean id="authenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="passwordEncoder">
<beans:bean class="org.springframework.security.authentication.encoding.ShaPasswordEncoder" />
</beans:property>
<beans:property name="userDetailsService" ref="userService" />
</beans:bean>
<beans:bean id="userService" class="com.awnry.springexample.UserDetailsServiceImpl" />
<beans:bean id="loginUrlAuthenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<beans:property name="loginFormUrl" value="/login" />
</beans:bean>
<beans:bean id="twoFactorAuthenticationFilter" class="com.awnry.springexample.TwoFactorAuthenticationFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationFailureHandler" ref="failureHandler" />
<beans:property name="authenticationSuccessHandler" ref="successHandler" />
<beans:property name="filterProcessesUrl" value="/processLogin" />
<beans:property name="postOnly" value="true" />
<beans:property name="extraParameter" value="domain" />
</beans:bean>
<beans:bean id="successHandler" class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="defaultTargetUrl" value="/login" />
</beans:bean>
<beans:bean id="failureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login?login_error=true" />
</beans:bean>
In twoFactorAuthenticationFilter bean definition, we set the extraParameter property to “domain” which is the name of the input field to use in our login form.
EDIT:
Have a look into constructors of User class.
If you don't know what a granted authority get into a look over this below link:
http://docs.spring.io/autorepo/docs/spring-security/3.2.1.RELEASE/apidocs/org/springframework/security/core/GrantedAuthority.html
Your coding gives a different mode applicable only for normal username and password. My code works for n factor authentication. Try switch over to my code if any problem keep on persists.
I'm very concious that this post has undergone 28 edits, so I may have missed some context. I'm also concious that you've amalgamated some code from the other answers into your question and that the problem has been somewhat "turned on its head" from "why won't a valid user authenticate?" to "why does every user authenticate?".
Current problem.
However, as written, your CustomAuthenticationProvider.authenticate() method will always return an Authentication object that returns auth.isAuthenticated() == true because you instantiate using this method which warns you about that very thing. Even if the collection you passed in as the third argument were empty, this would be the case. In fact, the collection always contains a GrantedAuthority for "registered", because pincodeEntered(name) always returns true. So, you need to correct your logic in those methods. authenticate() should return null if authentication is not successful.
Next steps
You've indicated in comments that what you want is a reference implementation of multi factor authentication. This is problematic - there is not necessarily agreement on what would constitute such a thing. For instance some would argue that multi factor should include a possession factor, rather than n knowledge factors on a single login page. It's also not really suited to an SO answer as it would need a blog post (or a series) - however generous the bounty.
There are working examples of multi factor authentication in spring on the web, here and here, for example. The latter I think you must have discovered as you appear to be using some of the code from there.
Making your CustomAuthenticationProvider work might take hours. Debugging might take even longer, as you have a mixture of methods in your example - it is not minimal. In particular, the TwoFactorAuthenticationFilter class is supposed to be used to intercept input on a request from the login page and concatenate the username and pin. In the example from the blog, this is set up in XML - you could add the security namespace to your business-config.xml and add those beans there for instance.
However, the SecurityConfig class and CustomAuthenticationProvider is a different method again.
Next, your project code references a j_security_check url, but that URL is not handled by anything. I am not sure of the intent behind that, or where it comes from. Finally, the MVC config for URL routing adds another element to the mix - one which I'm not familiar with.
I've played with your example for a while. There are too many mixed methods and too much complexity for me to fix quickly - maybe others can.
I strongly suggest that you start from the example in the blog exactly, then add the mvc config you want to over the top of that.
N.B. Setup for others trying to get the example to work
There were a couple of wrinkles in setting the project - it had an un-needed and unsatisfied dependency on javax.mail, you need to publish the maven dependencies to the server (in project->properties->deployment assembly) and you need to download and install adapters for the tomcat server if you don't already have it.
You also need to create the tables and columns in your database.
The easiest way to use java config for n-factor authentication is to start with a working example of single-factor authentication (username and password) that uses java config. Then you only have to make a few very minor changes: Assuming that you have a working single factor authentication app using java configuration, the steps are simply:
First, define layered roles, with one role for each factor. If you only have two factor authentication, keep your existing one role in the database, but then create a second role with full-access that you only assign at runtime. Thus, when the user logs in, they are logged in to the minimal role stored in the database, and that minimal role is only given access to one view, which is a form allowing them to enter a pin code that your controller just sent them via text or email or some other method. These layered roles get defined in SecurityConfig.java, as follows:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin()
.loginPage("/login")
.defaultSuccessUrl("/getpin")
.usernameParameter("j_username")
.passwordParameter("j_password")
.loginProcessingUrl("/j_spring_security_check")
.failureUrl("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.and()
.authorizeRequests()
.antMatchers("/getpin").hasAuthority("get_pin")
.antMatchers("/securemain/**").hasAuthority("full_access")
.antMatchers("/j_spring_security_check").permitAll()
.and()
.userDetailsService(userDetailsService);
}
}
Second, add code that upgrades the user's role to full-access upon successful entry of the correct pin code to the controller code that handles the pin code entry form POST. The code to manually assign full access in the controller is:
Role rl2 = new Role();rl2.setRole("full-access");//Don't save this one because we will manually assign it on login.
Set<Role> rls = new HashSet<Role>();
rls.add(rl2);
CustomUserDetailsService user = new CustomUserDetailsService(appService);
Authentication authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities(rls));
SecurityContextHolder.getContext().setAuthentication(authentication);
return "redirect:/securemain";
You can add as many layers as you want to after /getpin. You can also support multiple authorization roles and make it as complicated as you want to. But this answer gives the simplest way to get it running with java config.
Trying to build a Struts 2 app which directs the user to a page (Display.jsp) that shows the color of a user defined RGB color configuration. I get the example from the Struts 2 Tutorial by Budi Karniawan. When I manually cut and paste the source code and build the app manually as an NB Web application, it runs fine although the RGB parameters throw validation errors despite being input in the correct format (I checked that I am inputting using comma separated numbers for the RGB co-ordinates ie: green is 0,255,0). The directory structure is:
Then I decided to import the project file (creating a Web Application from Existing Sources option). I used the ant build.xml file to compile and run the application.
When I run the application through the app name:
http://localhost:8084/Budi7c
I get:
no Action mapped for namespace [/]
Then I append the action name mapped in struts.xml
http://localhost:8084/Budi7c/Design1.action
I get an HTTP 404. But the above Deisgn1.action reference worked when I tried to build the project manually. Can anyone please tell me the best way to correctly import and run this application given the following files? I would rather use an ant script and NOT MAVEN (since there seems to be a lot of issues building Struts 2 using Maven). I would just like to know a way to avoid the 404 error when trying to run struts actions.
If I try building the app manually, the input validation fails (even though I'm inputting the numbers and separating them with commas). If I try to import and use Ant to ensure a correct build, I end up with a 404.
The app is as follows:
web.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Restrict direct access to JSPs.
For the security constraint to work, the auth-constraint
and login-config elements must be present -->
<security-constraint>
<web-resource-collection>
<web-resource-name>JSPs</web-resource-name>
<url-pattern>/jsp/*</url-pattern>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
</web-app>
struts.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.enable.DynamicMethodInvocation" value="false" />
<constant name="struts.devMode" value="true" />
<package name="app07c" extends="struts-default">
<action name="Design1">
<result>/jsp/Design.jsp</result>
</action>
<action name="Design2" class="app07c.Design">
<result name="input">/jsp/Design.jsp</result>
<result name="success">/jsp/Display.jsp</result>
</action>
</package>
</struts>
Color.java:
package app07c;
import com.opensymphony.xwork2.ActionSupport;
public class Color extends ActionSupport {
private int red;
private int green;
private int blue;
public int getBlue() {
return blue;
}
public void setBlue(int blue) {
this.blue = blue;
}
public int getGreen() {
return green;
}
public void setGreen(int green) {
this.green = green;
}
public int getRed() {
return red;
}
public void setRed(int red) {
this.red = red;
}
public String getHexCode() {
return (red < 16? "0" : "")
+ Integer.toHexString(red)
+ (green < 16? "0" : "")
+ Integer.toHexString(green)
+ (blue < 16? "0" : "")
+ Integer.toHexString(blue);
}
}
Design.java:
package app07c;
import com.opensymphony.xwork2.ActionSupport;
public class Design extends ActionSupport {
private String designName;
private Color color;
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public String getDesignName() {
return designName;
}
public void setDesignName(String designName) {
this.designName = designName;
}
}
MyColorConverter.java:
package app07c.converter;
import java.util.Map;
import org.apache.struts2.util.StrutsTypeConverter;
import app07c.Color;
import com.opensymphony.xwork2.conversion.TypeConversionException;
public class MyColorConverter extends StrutsTypeConverter {
public Object convertFromString(Map context, String[] values,
Class toClass) {
boolean ok = false;
String rgb = values[0];
String[] colorComponents = rgb.split(",");
if (colorComponents != null
&& colorComponents.length == 3) {
String red = colorComponents[0];
String green = colorComponents[1];
String blue = colorComponents[2];
int redCode = 0;
int greenCode = 0;
int blueCode = 0;
try {
redCode = Integer.parseInt(red.trim());
greenCode = Integer.parseInt(green.trim());
blueCode = Integer.parseInt(blue.trim());
if (redCode >= 0 && redCode < 256
&& greenCode >= 0 && greenCode < 256
&& blueCode >= 0 && blueCode < 256) {
Color color = new Color();
color.setRed(redCode);
color.setGreen(greenCode);
color.setBlue(blueCode);
ok = true;
return color;
}
} catch (NumberFormatException e) {
}
}
if (!ok) {
throw new
TypeConversionException("Invalid color codes");
}
return null;
}
public String convertToString(Map context, Object o) {
Color color = (Color) o;
return color.getRed() + ","
+ color.getGreen() + ","
+ color.getBlue();
}
}
Design.jsp:
<%# taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Color</title>
<style type="text/css">#import url(css/main.css);</style>
<style>
.errorMessage {
color:red;
}
</style>
</head>
<body>
<div id="global" style="width:450px">
<h4>Color</h4>
Please enter the RGB components, each of which is
an integer between 0 and 255 (inclusive). Separate two components
with a comma. For example, green is 0,255,0.
<s:form action="Design2">
<s:textfield name="designName" label="Design Name"/>
<s:textfield name="color" label="Color"/>
<s:submit/>
</s:form>
</div>
</body>
</html>
Display.jsp:
<%# taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title>Design Details</title>
<style type="text/css">#import url(css/main.css);</style>
<style type="text/css">
.colorSample {
border:1px solid black;
width:100%;
height:100px;
background:#<s:property value="color.hexCode"/>;
}
</style>
</head>
<body>
<div id="global" style="width:250px">
<h4>Design details:</h4>
Design name: <s:property value="designName"/>
<br/>Color code: <s:property value="color"/>
<div class="colorSample"/>
</div>
</body>
</html>
I tried to change the web contents folder from /jsp to / so that project structure is the same as the directory structure. I then use the ant build script to compile and run the project and get the following stack:
ant -f C:\\struts2\\budi_ebook\\struts2extractb\\app07c -DforceRedeploy=false -Ddirectory.deployment.supported=true -Dnb.wait.for.caches=true run
init:
deps-module-jar:
deps-ear-jar:
deps-jar:
Warning: Program Files (x86)\F-Secure\Anti-Virus\aquarius\fa.log modified in the future.
Warning: Program Files\CommVault\Simpana\Log Files\CVD.log modified in the future.
Warning: Users\ManaarDC\NTUSER.DAT modified in the future.
Warning: Users\ManaarDC\ntuser.dat.LOG1 modified in the future.
Warning: Users\RedGuard_Admin.MANAARNET\AppData\Local\Temp\3\output1375645810208 modified in the future.
Warning: Users\RedGuard_Admin.MANAARNET\AppData\Local\Temp\3\toolbar_log.txt modified in the future.
Warning: Windows\Temp\avg_secure_search.log modified in the future.
Warning: app\ManaarDC\diag\rdbms\orcldw\orcldw\trace\orcldw_dbrm_3148.trc modified in the future.
Warning: app\ManaarDC\diag\rdbms\orcldw\orcldw\trace\orcldw_dbrm_3148.trm modified in the future.
Warning: app\ManaarDC\product\11.2.0\dbhome_1\D5H9RBP1.ManaarNet.com_orclDW\sysman\emd\agntstmp.txt modified in the future.
Warning: app\ManaarDC\product\11.2.0\dbhome_1\D5H9RBP1.ManaarNet.com_orclDW\sysman\log\emagent.trc modified in the future.
Warning: app\ManaarDC\product\11.2.0\dbhome_1\D5H9RBP1.ManaarNet.com_orclDW\sysman\log\emoms.log modified in the future.
Warning: app\ManaarDC\product\11.2.0\dbhome_1\D5H9RBP1.ManaarNet.com_orclDW\sysman\log\emoms.trc modified in the future.
Warning: app\ManaarDC\product\11.2.0\dbhome_1\oc4j\j2ee\OC4J_DBConsole_D5H9RBP1.ManaarNet.com_orclDW\log\em-application.log modified in the future.
Warning: inetpub\logs\LogFiles\W3SVC1\u_ex130804.log modified in the future.
C:\struts2\budi_ebook\struts2extractb\app07c\nbproject\build-impl.xml:841:
java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.Arrays.copyOfRange(Arrays.java:2694)
at java.lang.String.<init>(String.java:203)
at java.lang.String.substring(String.java:1913)
at java.util.StringTokenizer.nextToken(StringTokenizer.java:352)
at org.apache.tools.ant.util.FileUtils.normalize(FileUtils.java:741)
at org.apache.tools.ant.util.FileUtils.resolveFile(FileUtils.java:616)
at org.apache.tools.ant.types.resources.FileResource.<init>(FileResource.java:60)
at org.apache.tools.ant.util.SourceFileScanner$1.<init>(SourceFileScanner.java:96)
at org.apache.tools.ant.util.SourceFileScanner.restrict(SourceFileScanner.java:95)
at org.apache.tools.ant.taskdefs.Copy.buildMap(Copy.java:787)
at org.apache.tools.ant.taskdefs.Copy.scan(Copy.java:744)
at org.apache.tools.ant.taskdefs.Copy.iterateOverBaseDirs(Copy.java:666)
at org.apache.tools.ant.taskdefs.Copy.execute(Copy.java:563)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
at sun.reflect.GeneratedMethodAccessor90.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.Target.execute(Target.java:392)
at org.apache.tools.ant.Target.performTasks(Target.java:413)
at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1399)
at org.apache.tools.ant.Project.executeTarget(Project.java:1368)
at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
at org.apache.tools.ant.Project.executeTargets(Project.java:1251)
at org.apache.tools.ant.module.bridge.impl.BridgeImpl.run(BridgeImpl.java:283)
at org.apache.tools.ant.module.run.TargetExecutor.run(TargetExecutor.java:541)
at org.netbeans.core.execution.RunClassThread.run(RunClassThread.java:153)
BUILD FAILED (total time: 12 minutes 5 seconds)
Can't see the web content root directory from your project explorer because it's not a directory structure, it is a project structure. For example if you use maven then it should be [project root]/src/main/webapp. This directory should contain WEB-INF folder. If you have set web content root folder to /jsp in the project settings then it's wrong because it affects JSPs and other project files. You should set it to / instead. In this case the project root and the web content root would be the same or create a new folder in the project root folder say WebContent and place jsp, WEB-INF, and other web resources there. Set the web content root project settings to /WebContent. Then you could use /jsp/ in the result mappings.
Well here's how I solved it. I used the Netbeans 'Web Applications with Existing Sources' to import the project. For some reason the imported project doesn't register the 'jsp' directory. It just sees the JSP files in the Web Pages directory NOT Web Pages/jsp. So i simply removed the /jsp reference in the struts.xml. The app now runs fine and the validation errors are no longer there.
I'm happy with this answer to the extent that I can run the app, but I'm not happy that I fully understand how IDEs build these type of applications as the imported directory structure is clearly wrong (and missed the jsp folder). Would be grateful if anyone could shed further light on this or if I should post a separate question on the topic of building Struts2 in Netbeans
I'm using CookSwing (http://cookxml.yuanheng.org/cookswing/) to build a Java Swing UI, but the site doesn't have a lot of information. I'm stuck trying to get my Java class to retrieve the string from a textfield in the form (declared in XML). This should be easy to do, but nothing I've tried works. Does anyone have experience doing this? Here is the Java class:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import cookxml.cookswing.CookSwing;
public final class CookSwingForm
{
// Listener for the Quit button
public ActionListener exitAction = new ActionListener()
{
public void actionPerformed (ActionEvent e)
{
System.exit(0);
}
};
public CookSwingForm()
{
CookSwing cookSwing = new CookSwing(this);
cookSwing.render("sampleform.xml").setVisible(true);
}
public static void main(String[] args)
{
new CookSwingForm();
}
}
Here is the XML file ("sampleform.xml"):
<frame title="Sample Form" size="300,70"
defaultcloseoperation="EXIT_ON_CLOSE">
<borderlayout>
<constraint location="West">
<textfield columns="20" />
</constraint>
<constraint location="East">
<button text="Quit" actionlistener="exitAction" />
</constraint>
</borderlayout>
</frame>
I just need to have the Java class retrieve the string from the textfield that's declared in the XML file. Any help is greatly appreciated. Thanks!
I think you should use the id attribute and then use that as the variable name for the text field.
The xml would look as : <textfield id="box1" />
Here is what you have to do:
JTextField txtField = (JTextField) cookSwing.getId("box1").object;
//now,set some text
txtField.setText("Blah!");
//or get some text as you may wish
Also. Looks like the library is no longer being developed. That's a red flag right there.