├── .classpath ├── .project ├── pom.xml ├── src ├── main │ ├── java │ │ └── org │ │ │ └── meri │ │ │ └── simpleshirosecuredapplication │ │ │ ├── actions │ │ │ └── Actions.java │ │ │ ├── authc │ │ │ ├── PrimaryPrincipalSameAuthenticationStrategy.java │ │ │ ├── X509CertificateAuthenticationToken.java │ │ │ ├── X509CertificateOnlyAuthenticationToken.java │ │ │ └── X509CertificateUsernamePasswordToken.java │ │ │ ├── model │ │ │ ├── ModelProvider.java │ │ │ └── UserPersonalData.java │ │ │ ├── realm │ │ │ ├── JNDIAndSaltAwareJdbcRealm.java │ │ │ └── X509CertificateRealm.java │ │ │ ├── sanitizer │ │ │ └── Sanitizer.java │ │ │ └── servlet │ │ │ ├── AbstractFieldsHandlingServlet.java │ │ │ ├── AccountPageServlet.java │ │ │ ├── CertificateOrFormAuthenticationFilter.java │ │ │ ├── PerformFunctionAndGoBackServlet.java │ │ │ ├── ServletConstants.java │ │ │ └── VerboseFormAuthenticationFilter.java │ ├── resources │ │ ├── Shiro.ini │ │ ├── antisamy-tinymce-1.4.4.xml │ │ ├── db.changelog.xml │ │ ├── log4j.properties │ │ └── truststore │ └── webapp │ │ ├── WEB-INF │ │ ├── classes │ │ │ └── META-INF │ │ │ │ └── persistence.xml │ │ ├── jetty-web.xml │ │ └── web.xml │ │ ├── index.jsp │ │ └── simpleshirosecuredapplication │ │ ├── account │ │ ├── accessdenied.jsp │ │ ├── allapplicationfunctions.jsp │ │ ├── login.jsp │ │ ├── logout.jsp │ │ ├── personalaccountpage.jsp │ │ └── viewallaccounts.jsp │ │ ├── adminarea │ │ └── administratorspage.jsp │ │ ├── common │ │ └── commonformstuff.jsp │ │ ├── index.jsp │ │ ├── repairmen │ │ └── repairmen.jsp │ │ ├── sales │ │ └── sales.jsp │ │ └── scientists │ │ └── scientists.jsp └── test │ ├── java │ └── org │ │ └── meri │ │ └── simpleshirosecuredapplication │ │ ├── RunWaitTest.java │ │ ├── test │ │ └── AbstractContainerTest.java │ │ └── util │ │ ├── AnalyzeKeystore.java │ │ └── HashPassword.java │ └── resources │ ├── clients │ ├── administrator │ │ ├── administrator.cer │ │ ├── administrator.jks │ │ └── administrator.p12 │ ├── friendlyrepaiman │ │ ├── friendlyrepairman.cer │ │ ├── friendlyrepairman.jks │ │ └── friendlyrepairman.p12 │ ├── mathematician │ │ ├── mathematician.cer │ │ ├── mathematician.jks │ │ └── mathematician.p12 │ ├── physicien │ │ ├── physicien.cer │ │ ├── physicien.jks │ │ └── physicien.p12 │ ├── productsales │ │ ├── productsales.cer │ │ ├── productsales.jks │ │ └── productsales.p12 │ ├── servicessales │ │ ├── servicessales.cer │ │ ├── servicessales.jks │ │ └── servicessales.p12 │ └── unfriendlyrepaiman │ │ ├── unfriendlyrepairman.cer │ │ ├── unfriendlyrepairman.jks │ │ └── unfriendlyrepairman.p12 │ └── keystore └── target └── test-classes └── keystore /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | SimpleShiroSecuredApplication 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.maven.ide.eclipse.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.maven.ide.eclipse.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | SimpleSecuredApplication 5 | SimpleSecuredApplication 6 | war 7 | 0.0.1-SNAPSHOT 8 | SimpleSecuredApplication Maven Webapp 9 | http://maven.apache.org 10 | 11 | 12 | 13 | 1.6 14 | 1.6.1 15 | 1.2.0-SNAPSHOT 16 | 17 | 18 | 4.8.2 19 | 6.1.26 20 | 21 | 22 | 23 | 24 | org.liquibase 25 | liquibase-core 26 | 2.0.1 27 | 28 | 29 | org.apache.derby 30 | derby 31 | 10.7.1.1 32 | 33 | 34 | org.apache.shiro 35 | shiro-core 36 | ${shiro.version} 37 | 38 | 39 | org.apache.shiro 40 | shiro-web 41 | ${shiro.version} 42 | 43 | 44 | junit 45 | junit 46 | ${junit.version} 47 | test 48 | 49 | 50 | javax.servlet 51 | jstl 52 | 1.1.2 53 | 54 | 55 | javax.servlet 56 | servlet-api 57 | 2.5 58 | provided 59 | 60 | 61 | org.slf4j 62 | slf4j-log4j12 63 | ${slf4j.version} 64 | runtime 65 | 66 | 67 | log4j 68 | log4j 69 | 1.2.16 70 | runtime 71 | 72 | 73 | net.sourceforge.htmlunit 74 | htmlunit 75 | 2.6 76 | test 77 | 78 | 79 | org.mortbay.jetty 80 | jetty 81 | ${jetty.version} 82 | test 83 | 84 | 85 | org.mortbay.jetty 86 | jsp-2.1-jetty 87 | ${jetty.version} 88 | test 89 | 90 | 91 | org.mortbay.jetty 92 | jetty-naming 93 | ${jetty.version} 94 | test 95 | 96 | 97 | org.mortbay.jetty 98 | jetty-plus 99 | ${jetty.version} 100 | test 101 | 102 | 103 | taglibs 104 | standard 105 | 1.1.2 106 | 107 | 108 | org.apache.openjpa 109 | apache-openjpa 110 | 2.1.0 111 | pom 112 | compile 113 | 114 | 115 | org.owasp 116 | antisamy 117 | 1.4 118 | jar 119 | compile 120 | 121 | 122 | 123 | 124 | SimpleSecuredApplication 125 | 126 | 127 | 128 | maven-surefire-plugin 129 | 130 | never 131 | 132 | 133 | 134 | org.mortbay.jetty 135 | maven-jetty-plugin 136 | ${jetty.version} 137 | 138 | / 139 | 140 | 141 | 9080 142 | 60000 143 | 144 | 145 | 146 | ./target/yyyy_mm_dd.request.log 147 | 90 148 | true 149 | false 150 | GMT 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | maven2-repository.dev.java.net 161 | Java.net Repository for Maven 162 | http://download.java.net/maven/2/ 163 | default 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/actions/Actions.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.actions; 2 | 3 | import org.apache.shiro.SecurityUtils; 4 | import org.apache.shiro.authz.UnauthorizedException; 5 | 6 | public enum Actions { 7 | 8 | MANAGE_REPAIRMEN("MANAGE_REPAIRMEN", "functions:manage:repairmen"), MANAGE_SALES("MANAGE_SALES", "functions:manage:sales"), MANAGE_SCIENTISTS("MANAGE_SCIENTISTS", "functions:manage:scientists"), 9 | REPAIR_REFRIGERATOR("REPAIR_REFRIGERATOR", "functions:repair:refrigerator"), REPAIR_FRIDGE("REPAIR_FRIDGE", "functions:repair:bridge"), REPAIR_DOOR("REPAIR_DOOR", "functions:repair:door"), 10 | SALE_PRODUCT("SALE_PRODUCT", "functions:sale:product"), COLLECT_BONUS("COLLECT_BONUS", "functions:sale:collectbonus"), MEET_CUSTOMER("MEET_CUSTOMER", "functions:sale:meetcustomer"), 11 | RESEARCH_NEW_STUFF("RESEARCH_NEW_STUFF", "functions:science:research"), WRITE_ARTICLE("WRITE_ARTICLE", "functions:science:writearticle"), PREPARE_TALK("PREPARE_TALK", "functions:science:preparetalk"); 12 | 13 | public String doIt() { 14 | String neededPermission = getNeededPermission(); 15 | if (SecurityUtils.getSubject().isPermitted(neededPermission)) 16 | return "Function " + getName() + " run succesfully."; 17 | 18 | throw new UnauthorizedException("Logged user does not have " + neededPermission + " permission"); 19 | } 20 | 21 | private String getNeededPermission() { 22 | return permission; 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | private Actions(String name, String permission) { 30 | this.name = name; 31 | this.permission = permission; 32 | } 33 | 34 | private final String name; 35 | private final String permission; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/authc/PrimaryPrincipalSameAuthenticationStrategy.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.authc; 2 | 3 | import org.apache.shiro.authc.AuthenticationException; 4 | import org.apache.shiro.authc.AuthenticationInfo; 5 | import org.apache.shiro.authc.AuthenticationToken; 6 | import org.apache.shiro.authc.pam.AllSuccessfulStrategy; 7 | import org.apache.shiro.realm.Realm; 8 | import org.apache.shiro.subject.PrincipalCollection; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | public class PrimaryPrincipalSameAuthenticationStrategy extends AllSuccessfulStrategy { 13 | 14 | private static final Logger log = LoggerFactory.getLogger(PrimaryPrincipalSameAuthenticationStrategy.class); 15 | 16 | @Override 17 | public AuthenticationInfo afterAttempt(Realm realm, AuthenticationToken token, AuthenticationInfo info, AuthenticationInfo aggregate, Throwable t) { 18 | validatePrimaryPrincipals(info, aggregate, realm); 19 | return super.afterAttempt(realm, token, info, aggregate, t); 20 | } 21 | 22 | 23 | private void validatePrimaryPrincipals(AuthenticationInfo info, AuthenticationInfo aggregate, Realm realm) { 24 | PrincipalCollection aggregPrincipals = aggregate.getPrincipals(); 25 | if (aggregPrincipals==null || aggregPrincipals.isEmpty() || aggregPrincipals.getPrimaryPrincipal()==null) 26 | return ; 27 | 28 | if (info==null) 29 | return ; 30 | 31 | PrincipalCollection infoPrincipals = info.getPrincipals(); 32 | if (infoPrincipals==null || infoPrincipals.isEmpty() || infoPrincipals.getPrimaryPrincipal()==null) { 33 | String message = "Primary principal is missing from " + realm.getName() + " result."; 34 | log.debug(message); 35 | throw new AuthenticationException(message); 36 | } 37 | 38 | Object aggregPrincipal = aggregPrincipals.getPrimaryPrincipal(); 39 | Object infoPrincipal = infoPrincipals.getPrimaryPrincipal(); 40 | if (!aggregPrincipal.equals(infoPrincipal)) { 41 | String message = "All realms are required to return the same primary principal. Offending realm: " + realm.getName(); 42 | log.debug(message); 43 | throw new AuthenticationException(message); 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/authc/X509CertificateAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.authc; 2 | 3 | import java.security.cert.X509Certificate; 4 | 5 | import org.apache.shiro.authc.AuthenticationToken; 6 | 7 | public interface X509CertificateAuthenticationToken extends AuthenticationToken { 8 | 9 | public abstract X509Certificate getCertificate(); 10 | 11 | } -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/authc/X509CertificateOnlyAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.authc; 2 | 3 | import java.security.cert.X509Certificate; 4 | /** 5 | * Token for certificate based authentication. The certificate is used as both principal and credentials. 6 | * 7 | */ 8 | @SuppressWarnings("serial") 9 | public class X509CertificateOnlyAuthenticationToken implements X509CertificateAuthenticationToken { 10 | 11 | //certificate is used as both credentials and principal 12 | private X509Certificate certificate; 13 | 14 | public X509CertificateOnlyAuthenticationToken() { 15 | super(); 16 | } 17 | 18 | public X509CertificateOnlyAuthenticationToken(X509Certificate certificate) { 19 | super(); 20 | this.certificate = certificate; 21 | } 22 | 23 | @Override 24 | public Object getPrincipal() { 25 | return getCertificate(); 26 | } 27 | 28 | @Override 29 | public Object getCredentials() { 30 | return getCertificate(); 31 | } 32 | 33 | /* (non-Javadoc) 34 | * @see org.meri.simpleshirosecuredapplication.authc.X509CertificateAuthenticationToken#getCertificate() 35 | */ 36 | @Override 37 | public X509Certificate getCertificate() { 38 | return certificate; 39 | } 40 | 41 | public void setCertificate(X509Certificate certificate) { 42 | this.certificate = certificate; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/authc/X509CertificateUsernamePasswordToken.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.authc; 2 | 3 | import java.security.cert.X509Certificate; 4 | 5 | import org.apache.shiro.authc.UsernamePasswordToken; 6 | 7 | @SuppressWarnings("serial") 8 | public class X509CertificateUsernamePasswordToken extends UsernamePasswordToken implements X509CertificateAuthenticationToken { 9 | 10 | private X509Certificate certificate; 11 | 12 | public X509CertificateUsernamePasswordToken() { 13 | super(); 14 | } 15 | 16 | public X509CertificateUsernamePasswordToken(String username, String password) { 17 | super(username, password); 18 | } 19 | 20 | public X509CertificateUsernamePasswordToken(X509Certificate certificate) { 21 | super(); 22 | this.certificate = certificate; 23 | } 24 | 25 | public X509CertificateUsernamePasswordToken(String username, String password, X509Certificate certificate) { 26 | super(username, password); 27 | this.certificate = certificate; 28 | } 29 | 30 | public X509CertificateUsernamePasswordToken(String username, String password, boolean rememberMe, String host, X509Certificate certificate) { 31 | super(username, password, rememberMe, host); 32 | this.certificate = certificate; 33 | } 34 | 35 | @Override 36 | public X509Certificate getCertificate() { 37 | return certificate; 38 | } 39 | 40 | public void setCertificate(X509Certificate certificate) { 41 | this.certificate = certificate; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/model/ModelProvider.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.model; 2 | 3 | import java.util.List; 4 | 5 | import javax.persistence.EntityManager; 6 | import javax.persistence.EntityManagerFactory; 7 | import javax.persistence.Persistence; 8 | import javax.persistence.TypedQuery; 9 | 10 | import org.apache.shiro.SecurityUtils; 11 | 12 | public class ModelProvider { 13 | 14 | private EntityManager em; 15 | 16 | /** 17 | * Find data about logged user. 18 | * 19 | * @return current user data from database. If there is no such line, return null. 20 | */ 21 | public UserPersonalData getCurrentUserData() { 22 | EntityManager em = getEntityManager(); 23 | String loggedUser = (String)SecurityUtils.getSubject().getPrincipal(); 24 | UserPersonalData loggedUserData = em.find(UserPersonalData.class, loggedUser); 25 | return loggedUserData; 26 | } 27 | 28 | /** 29 | * Find data about all users. 30 | * 31 | * @return users data from database. 32 | */ public List getAllUsersData() { 33 | EntityManager em = getEntityManager(); 34 | TypedQuery query = em.createQuery("SELECT x FROM UserPersonalData x",UserPersonalData.class); 35 | List result = query.getResultList(); 36 | return result; 37 | } 38 | 39 | private EntityManager getEntityManager() { 40 | if (!hasActiveEntityManager()) { 41 | EntityManagerFactory emf = Persistence.createEntityManagerFactory("SimpleShiroSecuredApplicationPU"); 42 | em = emf.createEntityManager(); 43 | } 44 | return em; 45 | } 46 | 47 | public void close() { 48 | if (hasActiveEntityManager()) { 49 | em.close(); 50 | } 51 | } 52 | 53 | private boolean hasActiveEntityManager() { 54 | return em!=null && em.isOpen(); 55 | } 56 | 57 | public void beginTransaction() { 58 | getEntityManager().getTransaction().begin(); 59 | } 60 | 61 | public void persist(Object entity) { 62 | getEntityManager().persist(entity); 63 | } 64 | 65 | public void commit() { 66 | getEntityManager().getTransaction().commit(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/model/UserPersonalData.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.model; 2 | 3 | import javax.persistence.Column; 4 | import javax.persistence.Entity; 5 | import javax.persistence.Id; 6 | import javax.persistence.Table; 7 | 8 | @Entity 9 | @Table(name="user_personal_data") 10 | public class UserPersonalData { 11 | 12 | private String userName; 13 | private String firstname; 14 | private String lastname; 15 | private String about; 16 | 17 | 18 | @Column(name = "user_name") 19 | @Id 20 | public String getUserName() { 21 | return userName; 22 | } 23 | 24 | public void setUserName(String userName) { 25 | this.userName = userName; 26 | } 27 | 28 | @Column 29 | public String getFirstname() { 30 | return firstname; 31 | } 32 | 33 | public void setFirstname(String firstname) { 34 | this.firstname = firstname; 35 | } 36 | 37 | @Column 38 | public String getLastname() { 39 | return lastname; 40 | } 41 | 42 | public void setLastname(String lastname) { 43 | this.lastname = lastname; 44 | } 45 | 46 | @Column 47 | public String getAbout() { 48 | return about; 49 | } 50 | 51 | public void setAbout(String about) { 52 | this.about = about; 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/realm/JNDIAndSaltAwareJdbcRealm.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.realm; 2 | 3 | import java.sql.Connection; 4 | import java.sql.PreparedStatement; 5 | import java.sql.ResultSet; 6 | import java.sql.SQLException; 7 | 8 | import javax.naming.InitialContext; 9 | import javax.naming.NamingException; 10 | import javax.sql.DataSource; 11 | 12 | import org.apache.shiro.authc.AuthenticationException; 13 | import org.apache.shiro.authc.AuthenticationInfo; 14 | import org.apache.shiro.authc.AuthenticationToken; 15 | import org.apache.shiro.authc.SimpleAuthenticationInfo; 16 | import org.apache.shiro.authc.UsernamePasswordToken; 17 | import org.apache.shiro.authz.AuthorizationException; 18 | import org.apache.shiro.realm.jdbc.JdbcRealm; 19 | import org.apache.shiro.util.JdbcUtils; 20 | import org.apache.shiro.util.SimpleByteSource; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | 24 | /** 25 | * This realm has all {@link JdbcRealm} capabilities. It also supports JNDI as datasource source and 26 | * can add salt to passwords. 27 | */ 28 | public class JNDIAndSaltAwareJdbcRealm extends JdbcRealm { 29 | 30 | private static final Logger log = LoggerFactory.getLogger(JNDIAndSaltAwareJdbcRealm.class); 31 | 32 | protected String jndiDataSourceName; 33 | 34 | public JNDIAndSaltAwareJdbcRealm() { 35 | } 36 | 37 | public String getJndiDataSourceName() { 38 | return jndiDataSourceName; 39 | } 40 | 41 | public void setJndiDataSourceName(String jndiDataSourceName) { 42 | this.jndiDataSourceName = jndiDataSourceName; 43 | this.dataSource = getDataSourceFromJNDI(jndiDataSourceName); 44 | } 45 | 46 | private DataSource getDataSourceFromJNDI(String jndiDataSourceName) { 47 | try { 48 | InitialContext ic = new InitialContext(); 49 | return (DataSource) ic.lookup(jndiDataSourceName); 50 | } catch (NamingException e) { 51 | log.error("JNDI error while retrieving " + jndiDataSourceName, e); 52 | throw new AuthorizationException(e); 53 | } 54 | } 55 | 56 | @Override 57 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 58 | //identify account to log to 59 | UsernamePasswordToken userPassToken = (UsernamePasswordToken) token; 60 | String username = userPassToken.getUsername(); 61 | 62 | if (username == null) { 63 | log.debug("Username is null."); 64 | return null; 65 | } 66 | 67 | // read password hash and salt from db 68 | PasswdSalt passwdSalt = getPasswordForUser(username); 69 | 70 | if (passwdSalt == null) { 71 | log.debug("No account found for user [" + username + "]"); 72 | return null; 73 | } 74 | 75 | // return salted credentials 76 | SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, passwdSalt.password, getName()); 77 | info.setCredentialsSalt(new SimpleByteSource(passwdSalt.salt)); 78 | 79 | return info; 80 | } 81 | 82 | private PasswdSalt getPasswordForUser(String username) { 83 | PreparedStatement statement = null; 84 | ResultSet resultSet = null; 85 | Connection conn = null; 86 | try { 87 | conn = dataSource.getConnection(); 88 | statement = conn.prepareStatement(authenticationQuery); 89 | statement.setString(1, username); 90 | 91 | resultSet = statement.executeQuery(); 92 | 93 | boolean hasAccount = resultSet.next(); 94 | if (!hasAccount) 95 | return null; 96 | 97 | String salt = null; 98 | String password = resultSet.getString(1); 99 | if (resultSet.getMetaData().getColumnCount() > 1) 100 | salt = resultSet.getString(2); 101 | 102 | if (resultSet.next()) { 103 | throw new AuthenticationException("More than one user row found for user [" + username + "]. Usernames must be unique."); 104 | } 105 | 106 | return new PasswdSalt(password, salt); 107 | } catch (SQLException e) { 108 | final String message = "There was a SQL error while authenticating user [" + username + "]"; 109 | if (log.isErrorEnabled()) { 110 | log.error(message, e); 111 | } 112 | throw new AuthenticationException(message, e); 113 | 114 | } finally { 115 | JdbcUtils.closeResultSet(resultSet); 116 | JdbcUtils.closeStatement(statement); 117 | JdbcUtils.closeConnection(conn); 118 | } 119 | } 120 | 121 | } 122 | 123 | class PasswdSalt { 124 | 125 | public String password; 126 | public String salt; 127 | 128 | public PasswdSalt(String password, String salt) { 129 | super(); 130 | this.password = password; 131 | this.salt = salt; 132 | } 133 | 134 | } -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/realm/X509CertificateRealm.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.realm; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.IOException; 5 | import java.math.BigInteger; 6 | import java.security.KeyStore; 7 | import java.security.cert.CertPathBuilder; 8 | import java.security.cert.CertPathBuilderResult; 9 | import java.security.cert.CertStore; 10 | import java.security.cert.CertStoreParameters; 11 | import java.security.cert.CollectionCertStoreParameters; 12 | import java.security.cert.PKIXBuilderParameters; 13 | import java.security.cert.X509CRL; 14 | import java.security.cert.X509CertSelector; 15 | import java.security.cert.X509Certificate; 16 | import java.sql.Connection; 17 | import java.sql.PreparedStatement; 18 | import java.sql.ResultSet; 19 | import java.sql.SQLException; 20 | import java.util.Arrays; 21 | import java.util.Collection; 22 | import java.util.Collections; 23 | import java.util.List; 24 | 25 | import javax.naming.InitialContext; 26 | import javax.naming.NamingException; 27 | import javax.sql.DataSource; 28 | 29 | import org.apache.shiro.authc.AuthenticationException; 30 | import org.apache.shiro.authc.AuthenticationInfo; 31 | import org.apache.shiro.authc.AuthenticationToken; 32 | import org.apache.shiro.authc.SimpleAuthenticationInfo; 33 | import org.apache.shiro.authz.AuthorizationException; 34 | import org.apache.shiro.realm.Realm; 35 | import org.apache.shiro.util.JdbcUtils; 36 | import org.apache.shiro.util.Nameable; 37 | import org.apache.xerces.impl.dv.util.Base64; 38 | import org.meri.simpleshirosecuredapplication.authc.X509CertificateAuthenticationToken; 39 | import org.slf4j.Logger; 40 | import org.slf4j.LoggerFactory; 41 | 42 | public class X509CertificateRealm implements Realm, Nameable { 43 | 44 | protected static final String DEFAULT_ACCOUNT_TO_CERTIFICATE_QUERY = "select name from sec_users where serialnumber = ? and issuername = ?"; 45 | 46 | private String name; 47 | private String trustStorePassword; 48 | private String trustStore; 49 | 50 | protected String jndiDataSourceName; 51 | protected String accountToCertificateQuery = DEFAULT_ACCOUNT_TO_CERTIFICATE_QUERY; 52 | protected DataSource dataSource; 53 | 54 | private static final Logger log = LoggerFactory.getLogger(X509CertificateRealm.class); 55 | 56 | @Override 57 | public boolean supports(AuthenticationToken token) { 58 | if (token!=null) 59 | return token instanceof X509CertificateAuthenticationToken; 60 | 61 | return false; 62 | } 63 | 64 | @Override 65 | public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 66 | // the cast is legal, since Shiro will let in only X509CertificateAuthenticationToken tokens 67 | X509CertificateAuthenticationToken certificateToken = (X509CertificateAuthenticationToken) token; 68 | X509Certificate certificate = certificateToken.getCertificate(); 69 | 70 | // verify certificate 71 | if (!certificateOK(certificate)) { 72 | return null; 73 | } 74 | 75 | // the issuer name and serial number uniquely identifies certificate 76 | BigInteger serialNumber = certificate.getSerialNumber(); 77 | String issuerName = certificate.getIssuerDN().getName(); 78 | 79 | // find account associated with certificate 80 | String username = findUsernameToCertificate(issuerName, serialNumber); 81 | if (username == null) { 82 | // return null as no account was found 83 | return null; 84 | } 85 | 86 | // sucesfull verification, return authentication info 87 | return new SimpleAuthenticationInfo(username, certificate, getName()); 88 | } 89 | 90 | /** 91 | * The most simple certificate validation. If we are in web application context, the certificate is already checked 92 | * by web server and valid, so this method is useless there. 93 | * 94 | * @param certificate to be validated 95 | * 96 | * @return true if the certificate is valid. false otherwise. 97 | */ 98 | private boolean certificateOK(X509Certificate certificate) { 99 | if (certificate == null) 100 | return false; 101 | 102 | List chain = Arrays.asList(certificate); 103 | FileInputStream stream = null; 104 | 105 | /* Construct a valid path. */ 106 | try { 107 | stream = new FileInputStream(getTrustStore()); 108 | KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 109 | keyStore.load(stream, getTrustStorePassword().toCharArray()); 110 | 111 | X509CertSelector target = new X509CertSelector(); 112 | target.setCertificate(certificate); 113 | PKIXBuilderParameters params = new PKIXBuilderParameters(keyStore, target); 114 | CertStoreParameters intermediates = new CollectionCertStoreParameters(chain); 115 | params.addCertStore(CertStore.getInstance("Collection", intermediates)); 116 | 117 | // Lets ignore certificate revocation list for example purposes 118 | Collection crls = Collections.emptyList(); 119 | CertStoreParameters revoked = new CollectionCertStoreParameters(crls); 120 | params.addCertStore(CertStore.getInstance("Collection", revoked)); 121 | 122 | // If build() returns successfully, the certificate is valid. 123 | @SuppressWarnings("unused") 124 | CertPathBuilderResult r = CertPathBuilder.getInstance("PKIX").build(params); 125 | } catch (Exception ex) { 126 | log.debug("Certificate validation failed. ", ex); 127 | return false; 128 | } finally { 129 | try { 130 | stream.close(); 131 | } catch (IOException e) { 132 | log.error("Could not close truststore stream.", e); 133 | } 134 | } 135 | 136 | return true; 137 | } 138 | 139 | public String getTrustStorePassword() { 140 | return trustStorePassword; 141 | } 142 | 143 | public void setTrustStorePassword(String trustStorePassword) { 144 | this.trustStorePassword = trustStorePassword; 145 | } 146 | 147 | public String getTrustStore() { 148 | return trustStore; 149 | } 150 | 151 | public void setTrustStore(String trustStore) { 152 | this.trustStore = trustStore; 153 | } 154 | 155 | public String getAccountToCertificateQuery() { 156 | return accountToCertificateQuery; 157 | } 158 | 159 | public void setAccountToCertificateQuery(String accountToCertificateQuery) { 160 | this.accountToCertificateQuery = accountToCertificateQuery; 161 | } 162 | 163 | public String getJndiDataSourceName() { 164 | return jndiDataSourceName; 165 | } 166 | 167 | public void setJndiDataSourceName(String jndiDataSourceName) { 168 | this.jndiDataSourceName = jndiDataSourceName; 169 | this.dataSource = getDataSourceFromJNDI(jndiDataSourceName); 170 | } 171 | 172 | private DataSource getDataSourceFromJNDI(String jndiDataSourceName) { 173 | try { 174 | InitialContext ic = new InitialContext(); 175 | return (DataSource) ic.lookup(jndiDataSourceName); 176 | } catch (NamingException e) { 177 | log.error("JNDI error while retrieving " + jndiDataSourceName, e); 178 | throw new AuthorizationException(e); 179 | } 180 | } 181 | 182 | private String findUsernameToCertificate(String issuerName, BigInteger serialNumber) { 183 | String base64Serial = Base64.encode(serialNumber.toByteArray()); 184 | return getAccountName(issuerName, base64Serial); 185 | } 186 | 187 | private String getAccountName(String issuerName, String base64Serial) { 188 | PreparedStatement statement = null; 189 | ResultSet resultSet = null; 190 | Connection conn = null; 191 | try { 192 | conn = dataSource.getConnection(); 193 | statement = conn.prepareStatement(accountToCertificateQuery); 194 | statement.setString(1, base64Serial); 195 | statement.setString(2, issuerName); 196 | 197 | resultSet = statement.executeQuery(); 198 | 199 | boolean hasAccount = resultSet.next(); 200 | if (!hasAccount) 201 | return null; 202 | 203 | String username = resultSet.getString(1); 204 | 205 | if (resultSet.next()) { 206 | throw new AuthenticationException("More than one account for thre certificate."); 207 | } 208 | 209 | return username; 210 | } catch (SQLException e) { 211 | final String message = "There was a SQL error while authenticating user."; 212 | if (log.isErrorEnabled()) { 213 | log.error(message, e); 214 | } 215 | throw new AuthenticationException(message, e); 216 | 217 | } finally { 218 | JdbcUtils.closeResultSet(resultSet); 219 | JdbcUtils.closeStatement(statement); 220 | JdbcUtils.closeConnection(conn); 221 | } 222 | } 223 | 224 | public String getName() { 225 | return name; 226 | } 227 | 228 | public void setName(String name) { 229 | this.name = name; 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/sanitizer/Sanitizer.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.sanitizer; 2 | 3 | import java.io.InputStream; 4 | 5 | import org.owasp.validator.html.AntiSamy; 6 | import org.owasp.validator.html.CleanResults; 7 | import org.owasp.validator.html.Policy; 8 | import org.owasp.validator.html.PolicyException; 9 | import org.owasp.validator.html.ScanException; 10 | /** 11 | * Sanitize user input into XSS attack safe string. The class is thread safe. 12 | */ 13 | public class Sanitizer { 14 | 15 | private static final String POLICY_FILE_LOCATION = "/antisamy-tinymce-1.4.4.xml"; 16 | private static Policy policy; 17 | 18 | public Sanitizer() { 19 | } 20 | 21 | public String sanitize(String dirtyInput) { 22 | AntiSamy as = new AntiSamy(); 23 | try { 24 | CleanResults cr = as.scan(dirtyInput, getPolicy()); 25 | return cr.getCleanHTML(); 26 | } catch (ScanException e) { 27 | throw new RuntimeException(e); 28 | } catch (PolicyException e) { 29 | throw new RuntimeException(e); 30 | } 31 | } 32 | 33 | private Policy getPolicy() { 34 | if (policy==null) { 35 | try { 36 | InputStream resourceAsStream = getClass().getResourceAsStream(POLICY_FILE_LOCATION); 37 | policy = Policy.getInstance(resourceAsStream); 38 | } catch (PolicyException e) { 39 | throw new RuntimeException(e); 40 | } 41 | } 42 | return policy; 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/servlet/AbstractFieldsHandlingServlet.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.servlet; 2 | 3 | import javax.servlet.http.HttpServlet; 4 | import javax.servlet.http.HttpServletRequest; 5 | 6 | import org.meri.simpleshirosecuredapplication.sanitizer.Sanitizer; 7 | 8 | /** 9 | * Parent of all servlets reading user supplied data. Use {@link #getField(HttpServletRequest, String)} 10 | * to get fields values. 11 | * 12 | */ 13 | @SuppressWarnings("serial") 14 | public abstract class AbstractFieldsHandlingServlet extends HttpServlet { 15 | 16 | private Sanitizer sanitizer = new Sanitizer(); 17 | 18 | public AbstractFieldsHandlingServlet() { 19 | super(); 20 | } 21 | 22 | /** 23 | * Reads sanitized request parameter value from request field. 24 | * 25 | * @param request http request 26 | * @param parameter name of request parameter 27 | * 28 | * @return request parameter value 29 | */ 30 | protected String getField(HttpServletRequest request, String parameter) { 31 | String dirtyValue = request.getParameter(parameter); 32 | return sanitizer.sanitize(dirtyValue); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/servlet/AccountPageServlet.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.servlet; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.RequestDispatcher; 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.apache.shiro.SecurityUtils; 11 | import org.meri.simpleshirosecuredapplication.model.ModelProvider; 12 | import org.meri.simpleshirosecuredapplication.model.UserPersonalData; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | @SuppressWarnings("serial") 17 | public class AccountPageServlet extends AbstractFieldsHandlingServlet { 18 | 19 | private static final Logger log = LoggerFactory.getLogger(AccountPageServlet.class); 20 | 21 | @Override 22 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 23 | doPost(req, resp); 24 | } 25 | 26 | @Override 27 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 28 | String loggedPrincipal = (String) SecurityUtils.getSubject().getPrincipal(); 29 | String firstname = getField(request, "firstname"); 30 | String lastname = getField(request, "lastname"); 31 | String about = getField(request, "about"); 32 | 33 | try { 34 | saveData(loggedPrincipal, firstname, lastname, about); 35 | request.setAttribute(ServletConstants.actionResultMessage, "Saved was successful."); 36 | } catch (Exception ex) { 37 | log.error("Could not save data.", ex); 38 | request.setAttribute(ServletConstants.actionResultMessage, "Saved unsuccessful :(."); 39 | } 40 | 41 | // forward the request and response back to original page 42 | String originalPage = request.getParameter(ServletConstants.originalPage); 43 | RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(originalPage); 44 | dispatcher.forward(request, response); 45 | } 46 | 47 | private void saveData(String loggedPrincipal, String firstname, String lastname, String about) { 48 | ModelProvider mp = new ModelProvider(); 49 | try { 50 | UserPersonalData data = mp.getCurrentUserData(); 51 | mp.beginTransaction(); 52 | if (data == null) { 53 | data = new UserPersonalData(); 54 | updateUser(data, loggedPrincipal, firstname, lastname, about); 55 | mp.persist(data); 56 | } else { 57 | updateUser(data, loggedPrincipal, firstname, lastname, about); 58 | } 59 | mp.commit(); 60 | } finally { 61 | mp.close(); 62 | } 63 | } 64 | 65 | private void updateUser(UserPersonalData data, String loggedPrincipal, String firstname, String lastname, String about) { 66 | data.setUserName(loggedPrincipal); 67 | data.setFirstname(firstname); 68 | data.setLastname(lastname); 69 | data.setAbout(about); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/servlet/CertificateOrFormAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.servlet; 2 | 3 | import java.security.cert.X509Certificate; 4 | 5 | import javax.servlet.ServletRequest; 6 | import javax.servlet.ServletResponse; 7 | 8 | import org.apache.shiro.authc.AuthenticationException; 9 | import org.apache.shiro.authc.AuthenticationToken; 10 | import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; 11 | import org.meri.simpleshirosecuredapplication.authc.X509CertificateUsernamePasswordToken; 12 | 13 | public class CertificateOrFormAuthenticationFilter extends FormAuthenticationFilter { 14 | 15 | @Override 16 | protected boolean isLoginSubmission(ServletRequest request, ServletResponse response) { 17 | return super.isLoginSubmission(request, response) || isCertificateLogInAttempt(request, response); 18 | } 19 | 20 | private boolean isCertificateLogInAttempt(ServletRequest request, ServletResponse response) { 21 | return hasCertificate(request) && !getSubject(request, response).isAuthenticated(); 22 | } 23 | 24 | private boolean hasCertificate(ServletRequest request) { 25 | return null != getCertificate(request); 26 | } 27 | 28 | private X509Certificate getCertificate(ServletRequest request) { 29 | X509Certificate[] attribute = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate"); 30 | return attribute == null ? null : attribute[0]; 31 | } 32 | 33 | @Override 34 | protected AuthenticationToken createToken(String username, String password, ServletRequest request, ServletResponse response) { 35 | boolean rememberMe = isRememberMe(request); 36 | String host = getHost(request); 37 | X509Certificate certificate = getCertificate(request); 38 | return createToken(username, password, rememberMe, host, certificate); 39 | } 40 | 41 | protected AuthenticationToken createToken(String username, String password, boolean rememberMe, String host, X509Certificate certificate) { 42 | return new X509CertificateUsernamePasswordToken(username, password, rememberMe, host, certificate); 43 | } 44 | 45 | @Override 46 | protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) { 47 | String message = ae.getMessage(); 48 | request.setAttribute(getFailureKeyAttribute(), message); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/servlet/PerformFunctionAndGoBackServlet.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.servlet; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.RequestDispatcher; 6 | import javax.servlet.Servlet; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServlet; 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | import org.apache.log4j.Logger; 13 | import org.apache.shiro.ShiroException; 14 | import org.meri.simpleshirosecuredapplication.actions.Actions; 15 | 16 | public class PerformFunctionAndGoBackServlet extends HttpServlet implements Servlet { 17 | 18 | private static transient final Logger log = Logger.getLogger(PerformFunctionAndGoBackServlet.class); 19 | 20 | private static final long serialVersionUID = -7896114563632467947L; 21 | 22 | @Override 23 | protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { 24 | doPost(req, resp); 25 | } 26 | 27 | @Override 28 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 29 | String action = request.getParameter("action"); 30 | String actionResult = performAction(action); 31 | request.setAttribute(ServletConstants.actionResultMessage, actionResult); 32 | 33 | // forward the request and response back to original page 34 | String originalPage = request.getParameter(ServletConstants.originalPage); 35 | RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(originalPage); 36 | dispatcher.forward(request, response); 37 | } 38 | 39 | private String performAction(String actionName) { 40 | try { 41 | Actions action = findAction(actionName); 42 | String result = action == null ? null : action.doIt(); 43 | log.debug("Performed function with result: " + result); 44 | return result; 45 | } catch (ShiroException ex) { 46 | log.debug("Function failed with " + ex.getMessage() + " message."); 47 | return "Error: " + ex.getMessage(); 48 | } 49 | } 50 | 51 | private Actions findAction(String actionName) { 52 | if (actionName == null) 53 | return null; 54 | 55 | Actions[] values = Actions.values(); 56 | 57 | for (Actions action : values) { 58 | if (actionName.equals(action.getName())) 59 | return action; 60 | } 61 | return null; 62 | } 63 | 64 | public String getUrl(HttpServletRequest request) { 65 | String reqUrl = request.getRequestURL().toString(); 66 | String queryString = request.getQueryString(); // d=789 67 | if (queryString != null) { 68 | reqUrl += "?" + queryString; 69 | } 70 | return reqUrl; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/servlet/ServletConstants.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.servlet; 2 | 3 | public class ServletConstants { 4 | 5 | public static final String originalPage = "originalPage"; 6 | public static final String actionResultMessage = "actionResultMessage"; 7 | 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/org/meri/simpleshirosecuredapplication/servlet/VerboseFormAuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.servlet; 2 | 3 | import javax.servlet.ServletRequest; 4 | 5 | import org.apache.shiro.authc.AuthenticationException; 6 | import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; 7 | 8 | public class VerboseFormAuthenticationFilter extends FormAuthenticationFilter { 9 | 10 | @Override 11 | protected void setFailureAttribute(ServletRequest request, AuthenticationException ae) { 12 | String message = ae.getMessage(); 13 | request.setAttribute(getFailureKeyAttribute(), message); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/Shiro.ini: -------------------------------------------------------------------------------- 1 | [main] 2 | # realms to be used 3 | certificateRealm=org.meri.simpleshirosecuredapplication.realm.X509CertificateRealm 4 | certificateRealm.trustStore=src/main/resources/truststore 5 | certificateRealm.trustStorePassword=secret 6 | certificateRealm.jndiDataSourceName=jdbc/SimpleShiroSecuredApplicationDB 7 | 8 | saltedJdbcRealm=org.meri.simpleshirosecuredapplication.realm.JNDIAndSaltAwareJdbcRealm 9 | # any object property is automatically configurable in Shiro.ini file 10 | saltedJdbcRealm.jndiDataSourceName=jdbc/SimpleShiroSecuredApplicationDB 11 | # the realm should handle also authorization 12 | saltedJdbcRealm.permissionsLookupEnabled=true 13 | # If not filled, subclasses of JdbcRealm assume "select password from users where username = ?" 14 | # first result column is password, second result column is salt 15 | saltedJdbcRealm.authenticationQuery = select password, salt from sec_users where name = ? 16 | # If not filled, subclasses of JdbcRealm assume "select role_name from user_roles where username = ?" 17 | saltedJdbcRealm.userRolesQuery = select role_name from sec_users_roles where user_name = ? 18 | # If not filled, subclasses of JdbcRealm assume "select permission from roles_permissions where role_name = ?" 19 | saltedJdbcRealm.permissionsQuery = select permissions from sec_roles_permissions where role_name = ? 20 | 21 | # password hashing specification, put something big for hasIterations 22 | sha256Matcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher 23 | sha256Matcher.hashAlgorithmName=SHA-256 24 | sha256Matcher.hashIterations=1 25 | saltedJdbcRealm.credentialsMatcher = $sha256Matcher 26 | 27 | # filter configuration 28 | certificateFilter = org.meri.simpleshirosecuredapplication.servlet.CertificateOrFormAuthenticationFilter 29 | # request parameter with login error information; if not present filter assumes 'shiroLoginFailure' 30 | certificateFilter.failureKeyAttribute=simpleShiroApplicationLoginFailure 31 | # specify login page 32 | certificateFilter.loginUrl = /simpleshirosecuredapplication/account/login.jsp 33 | # name of request parameter with username; if not present filter assumes 'username' 34 | certificateFilter.usernameParam = user 35 | # name of request parameter with password; if not present filter assumes 'password' 36 | certificateFilter.passwordParam = pass 37 | # does the user wish to be remembered?; if not present filter assumes 'rememberMe' 38 | certificateFilter.rememberMeParam = remember 39 | # redirect after successful login 40 | certificateFilter.successUrl = /simpleshirosecuredapplication/account/personalaccountpage.jsp 41 | 42 | # roles filter: redirect to error page if user does not have access rights 43 | roles.unauthorizedUrl = /simpleshirosecuredapplication/account/accessdenied.jsp 44 | 45 | # multi-realms strategy 46 | # authenticationStrategy=org.meri.simpleshirosecuredapplication.authc.PrimaryPrincipalSameAuthenticationStrategy 47 | # securityManager.authenticator.authenticationStrategy=$authenticationStrategy 48 | 49 | [urls] 50 | # force ssl for login page 51 | /simpleshirosecuredapplication/account/login.jsp=ssl[8443], certificateFilter 52 | 53 | # only users with some roles are allowed to use role-specific pages 54 | /simpleshirosecuredapplication/repairmen/**=certificateFilter, roles[repairman] 55 | /simpleshirosecuredapplication/sales/**=certificateFilter, roles[sales] 56 | /simpleshirosecuredapplication/scientists/**=certificateFilter, roles[scientist] 57 | /simpleshirosecuredapplication/adminarea/**=certificateFilter, roles[Administrator] 58 | 59 | # enable certificateFilter filter for all application pages 60 | /simpleshirosecuredapplication/**=certificateFilter 61 | -------------------------------------------------------------------------------- /src/main/resources/antisamy-tinymce-1.4.4.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 28 | 29 | 30 | 32 | 33 | 34 | 36 | 37 | 39 | 40 | 41 | 42 | 49 | 50 | 54 | 55 | 56 | 57 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 81 | 82 | 83 | 84 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 97 | 98 | 99 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | g 115 | grin 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /src/main/resources/db.changelog.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | 10 | Create table structure for users, passwords, roles and permissions. 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 44 | 45 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 61 | 62 | 63 | 64 | 65 | 66 | Create initial users, roles and permissions. 67 | 68 | 69 | 70 | 72 | 73 | 74 | 75 | 76 | 78 | 79 | 80 | 81 | 82 | 84 | 85 | 86 | 87 | 88 | 90 | 91 | 92 | 93 | 94 | 96 | 97 | 98 | 99 | 100 | 102 | 103 | 104 | 105 | 106 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | Add certificate unique id to sec_users table. 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | Associate certificates with initial user accounts. 185 | 186 | 187 | 188 | 189 | name='administrator' 190 | 191 | 192 | 193 | 194 | name='friendlyrepairman' 195 | 196 | 197 | 198 | 199 | name='unfriendlyrepairman' 200 | 201 | 202 | 203 | 204 | name='mathematician' 205 | 206 | 207 | 208 | 209 | name='servicessales' 210 | 211 | 212 | 213 | 214 | name='productsales' 215 | 216 | 217 | 218 | 219 | name='physicien' 220 | 221 | 222 | 223 | 224 | 225 | Add user personal data such as first name, last name, hobby etc to the application. 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, 13 | # software distributed under the License is distributed on an 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | # KIND, either express or implied. See the License for the 16 | # specific language governing permissions and limitations 17 | # under the License. 18 | # 19 | # This file is used to format all logging output 20 | log4j.rootLogger=TRACE, stdout 21 | 22 | log4j.appender.stdout=org.apache.log4j.ConsoleAppender 23 | log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 24 | log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%c]: %m%n 25 | 26 | # ============================================================================= 27 | # 3rd Party Libraries 28 | # OFF, FATAL, ERROR, WARN, INFO, DEBUG, ALL 29 | # ============================================================================= 30 | # ehcache caching manager: 31 | log4j.logger.net.sf.ehcache=WARN 32 | 33 | # Most all Apache libs: 34 | log4j.logger.org.apache=WARN 35 | 36 | # Quartz Enterprise Scheular (java 'cron' utility) 37 | log4j.logger.org.quartz=WARN 38 | 39 | # ============================================================================= 40 | # Apache Shiro 41 | # ============================================================================= 42 | # Shiro security framework 43 | log4j.logger.org.apache.shiro=TRACE 44 | #log4j.logger.org.apache.shiro.realm.text.PropertiesRealm=INFO 45 | #log4j.logger.org.apache.shiro.cache.ehcache.EhCache=INFO 46 | #log4j.logger.org.apache.shiro.io=INFO 47 | #log4j.logger.org.apache.shiro.web.servlet=INFO 48 | log4j.logger.org.apache.shiro.util.ThreadContext=INFO 49 | -------------------------------------------------------------------------------- /src/main/resources/truststore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/main/resources/truststore -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/classes/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 3 | 5 | org.apache.openjpa.persistence.PersistenceProviderImpl 6 | 7 | jdbc/SimpleShiroSecuredApplicationDB 8 | 9 | org.meri.simpleshirosecuredapplication.model.UserPersonalData 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/jetty-web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | jdbc/SimpleShiroSecuredApplicationDB 7 | 8 | 9 | ../SimpleShiroSecuredApplicationDatabase 10 | create 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Simple Secure Application 7 | 8 | 9 | 10 | PerformFunctionAndGoBackServlet 11 | org.meri.simpleshirosecuredapplication.servlet.PerformFunctionAndGoBackServlet 12 | 1 13 | 14 | 15 | 16 | AccountPageServlet 17 | org.meri.simpleshirosecuredapplication.servlet.AccountPageServlet 18 | 1 19 | 20 | 21 | 22 | PerformFunctionAndGoBackServlet 23 | /simpleshirosecuredapplication/masterservlet 24 | 25 | 26 | 27 | AccountPageServlet 28 | /simpleshirosecuredapplication/accountpageservlet 29 | 30 | 31 | 32 | index.jsp 33 | 34 | 35 | 36 | 37 | 38 | ShiroFilter 39 | org.apache.shiro.web.servlet.IniShiroFilter 40 | 41 | configPath 42 | classpath:Shiro.ini 43 | 44 | 45 | 46 | 47 | ShiroFilter 48 | /* 49 | 50 | 51 | 52 | 53 | 54 | 55 | Derby Connection 56 | jdbc/SimpleShiroSecuredApplicationDB 57 | javax.sql.DataSource 58 | Container 59 | 60 | 61 | 62 | 63 | liquibase.changelog 64 | src/main/resources/db.changelog.xml 65 | 66 | 67 | 68 | liquibase.datasource 69 | jdbc/SimpleShiroSecuredApplicationDB 70 | 71 | 72 | 74 | 75 | liquibase.integration.servlet.LiquibaseServletListener 76 | 77 | 78 | 79 | persistence/SimpleShiroSecuredApplicationPU 80 | SimpleShiroSecuredApplicationPU 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/main/webapp/index.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 4 |

Welcome

5 | Simple web application for trying out Shiro security framework. There is no security yet.
6 | ">login
7 | ">all application functions
8 | ">view all accounts
9 | ">personal account page
10 | ">administrators page
11 | ">repairmen page
12 | ">sales page
13 | ">scientists page
14 | ">logout
15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/account/accessdenied.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | 4 | 5 | 6 | 7 | Access Denied 8 | 9 | 10 | <%@include file="/simpleshirosecuredapplication/common/commonformstuff.jsp" %> 11 | Sorry, you do not have access rights to that area. 12 | 13 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/account/allapplicationfunctions.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | 4 | 5 | 6 | 7 | All Application Functions 8 | 9 | 10 | <%@page import="org.meri.simpleshirosecuredapplication.actions.Actions"%> 11 | 12 |
13 | <%@include file="/simpleshirosecuredapplication/common/commonformstuff.jsp" %> 14 | All functions available in the application: 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | <% for (Actions action : Actions.values()) { %> 24 | 25 | 26 | 27 | 28 | <% } %> 29 | 30 |
Function NameDo It
<%=action.getName() %>
31 |
32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/account/login.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | 4 | 5 | 6 | 7 | Please Log In 8 | 9 | 10 | <%@include file="/simpleshirosecuredapplication/common/commonformstuff.jsp" %> 11 | <% 12 | String errorDescription = (String) request.getAttribute("simpleShiroApplicationLoginFailure"); 13 | if (errorDescription!=null) { 14 | %> 15 | Login attempt was unsuccessful: <%=errorDescription%> 16 | <% 17 | } 18 | %>
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |
Username:
Password:
Remember Me
35 |
36 | 37 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/account/logout.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | 4 | 5 | 6 | 7 | Logout 8 | 9 | 10 | <%@include file="/simpleshirosecuredapplication/common/commonformstuff.jsp" %> 11 | <%@ page import="org.apache.shiro.SecurityUtils" %> 12 | <% SecurityUtils.getSubject().logout();%> 13 |

You have successfully logged out. 14 | 15 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/account/personalaccountpage.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | 4 | 5 | 6 | 13 | 14 | Personal Account 15 | 16 | 17 |

18 | <%@page import="org.apache.shiro.SecurityUtils"%> 19 | <%@include file="/simpleshirosecuredapplication/common/commonformstuff.jsp"%> 20 | <%@ page import="org.meri.simpleshirosecuredapplication.model.ModelProvider"%> 21 | <%@ page import="org.meri.simpleshirosecuredapplication.model.UserPersonalData"%> 22 | <% 23 | ModelProvider mp = new ModelProvider(); 24 | UserPersonalData loggedUserData = mp.getCurrentUserData(); 25 | mp.close(); 26 | String loggedUser = (String)SecurityUtils.getSubject().getPrincipal(); 27 | String firstname = loggedUserData==null || loggedUserData.getFirstname() == null? "" : loggedUserData.getFirstname(); 28 | String lastname = loggedUserData==null || loggedUserData.getLastname() == null? "" : loggedUserData.getLastname(); 29 | String about = loggedUserData==null ? null : loggedUserData.getAbout(); 30 | %> 31 | Hi <%=loggedUser%>. You can edit all your data here.
32 |
33 |
34 |
35 |
37 | 38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/account/viewallaccounts.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 4 | 5 | 6 | 7 | 8 | 9 | View All Accounts 10 | 11 | 12 | <%@include file="/simpleshirosecuredapplication/common/commonformstuff.jsp" %> 13 | <%@page import="org.apache.shiro.SecurityUtils"%> 14 | <%@page import="org.meri.simpleshirosecuredapplication.model.ModelProvider"%> 15 | <%@page import="org.meri.simpleshirosecuredapplication.model.UserPersonalData"%> 16 | <%@page import="java.util.List"%> 17 | All users with filled account data: 18 | <% 19 | ModelProvider mp = new ModelProvider(); 20 | List usersData = mp.getAllUsersData(); 21 | pageContext.setAttribute("allusers", usersData); 22 | mp.close(); 23 | %> 24 |
    25 | <% 26 | for (UserPersonalData data : usersData) { 27 | %> 28 |
  • <%=data.getFirstname()%> <%=data.getLastname()%>
    29 | 30 |
  • 31 | <% } %> 32 |
33 | 34 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/adminarea/administratorspage.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | 4 | 5 | 6 | 7 | Administrator Page 8 | 9 | 10 | <%@page import="org.meri.simpleshirosecuredapplication.actions.Actions"%> 11 | 12 |
13 | <%@include file="/simpleshirosecuredapplication/common/commonformstuff.jsp" %> 14 |

Administrator Page

15 | This page is meant for administrators only. If you are not one, please go away. Available functions: 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
Function NameDo It
Manage Repairmen:
Manage Sales:
Manage Scientists:
38 |
39 | 40 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/common/commonformstuff.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> 2 | ">Go Home 3 | ">logout
4 | <% 5 | String reqUrl = request.getServletPath().toString(); 6 | String actionMessage = (String) request.getAttribute("actionResultMessage"); 7 | if (actionMessage!=null) { 8 | %> 9 |
  • <%=actionMessage %>

  • 10 | <%} %> 11 | 12 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/index.jsp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/repairmen/repairmen.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | 4 | 5 | 6 | 7 | Repairmen Page 8 | 9 | 10 | <%@page import="org.meri.simpleshirosecuredapplication.actions.Actions"%> 11 | 12 |
    13 | <%@include file="/simpleshirosecuredapplication/common/commonformstuff.jsp" %> 14 |

    Repairmen Page

    15 | This page is meant for repairmen only. If you are not one, please go away. Available functions: 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
    Function NameDo It
    Repair refrigerator:
    Repair fridge:
    Repair door:
    38 |
    39 | 40 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/sales/sales.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | 4 | 5 | 6 | 7 | Sales Page 8 | 9 | 10 | <%@page import="org.meri.simpleshirosecuredapplication.actions.Actions"%> 11 | 12 |
    13 | <%@include file="/simpleshirosecuredapplication/common/commonformstuff.jsp" %> 14 |

    Sales Page

    15 | This page is meant for sales only. If you are not one, please go away. Available functions: 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
    Function NameDo It
    Sale product:
    Collect bonus:
    Meet customer:
    38 |
    39 | 40 | -------------------------------------------------------------------------------- /src/main/webapp/simpleshirosecuredapplication/scientists/scientists.jsp: -------------------------------------------------------------------------------- 1 | <%@ page language="java" contentType="text/html; charset=ISO-8859-1" 2 | pageEncoding="ISO-8859-1"%> 3 | 4 | 5 | 6 | 7 | Scientists Page 8 | 9 | 10 | <%@page import="org.meri.simpleshirosecuredapplication.actions.Actions"%> 11 | 12 |
    13 | <%@include file="/simpleshirosecuredapplication/common/commonformstuff.jsp" %> 14 |

    Scientists Page

    15 | This page is meant for scientists only. If you are not one, please go away. Available functions: 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
    Function NameDo It
    Research:
    Write article:
    Prepare talk:
    38 |
    39 | 40 | -------------------------------------------------------------------------------- /src/test/java/org/meri/simpleshirosecuredapplication/RunWaitTest.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication; 2 | 3 | import java.io.IOException; 4 | import java.net.MalformedURLException; 5 | 6 | import org.junit.Test; 7 | import org.meri.simpleshirosecuredapplication.test.AbstractContainerTest; 8 | 9 | import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; 10 | 11 | public class RunWaitTest extends AbstractContainerTest { 12 | 13 | @Test 14 | public void sleepForever() throws FailingHttpStatusCodeException, MalformedURLException, IOException, InterruptedException { 15 | while (true) 16 | Thread.sleep(9999); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/test/java/org/meri/simpleshirosecuredapplication/test/AbstractContainerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package org.meri.simpleshirosecuredapplication.test; 20 | 21 | import static org.junit.Assert.assertTrue; 22 | 23 | import org.junit.Before; 24 | import org.junit.BeforeClass; 25 | import org.mortbay.jetty.Connector; 26 | import org.mortbay.jetty.Server; 27 | import org.mortbay.jetty.nio.SelectChannelConnector; 28 | import org.mortbay.jetty.security.SslSocketConnector; 29 | import org.mortbay.jetty.webapp.WebAppContext; 30 | 31 | import com.gargoylesoftware.htmlunit.WebClient; 32 | 33 | public abstract class AbstractContainerTest { 34 | protected static PauseableServer server; 35 | 36 | protected static final int port = 9180; 37 | protected static final int sslPort = 8443; 38 | 39 | protected static final String BASEURI = "http://localhost:" + port + "/"; 40 | 41 | protected final WebClient webClient = new WebClient(); 42 | 43 | private static String[] configurationClasses = 44 | { 45 | "org.mortbay.jetty.webapp.JettyWebXmlConfiguration", 46 | "org.mortbay.jetty.webapp.WebInfConfiguration", 47 | "org.mortbay.jetty.plus.webapp.EnvConfiguration", 48 | "org.mortbay.jetty.plus.webapp.Configuration", 49 | "org.mortbay.jetty.webapp.TagLibConfiguration" 50 | } ; 51 | 52 | 53 | @BeforeClass 54 | public static void startContainer() throws Exception { 55 | if (server == null) { 56 | server = new PauseableServer(); 57 | Connector connector = createHttpConnector(); 58 | SslSocketConnector sslConnector = createSslConnector(); 59 | server.setConnectors(new Connector[]{connector, sslConnector}); 60 | 61 | WebAppContext context = createWebAppContext(); 62 | server.setHandler(context); 63 | 64 | server.start(); 65 | assertTrue(server.isStarted()); 66 | } 67 | } 68 | 69 | private static WebAppContext createWebAppContext() { 70 | WebAppContext context = new WebAppContext("src/main/webapp", "/"); 71 | context.setConfigurationClasses(configurationClasses); 72 | 73 | return context; 74 | } 75 | 76 | private static SslSocketConnector createSslConnector() { 77 | SslSocketConnector sslConnector = new SslSocketConnector(); 78 | sslConnector.setPort(sslPort); 79 | sslConnector.setKeyPassword("secret"); 80 | sslConnector.setKeystore("src/test/resources/keystore"); 81 | sslConnector.setTrustPassword("secret"); 82 | sslConnector.setTruststore("src/main/resources/truststore"); 83 | sslConnector.setPassword("secret"); 84 | sslConnector.setWantClientAuth(true); 85 | //sslConnector.setNeedClientAuth(true); 86 | 87 | return sslConnector; 88 | } 89 | 90 | private static Connector createHttpConnector() { 91 | Connector connector = new SelectChannelConnector(); 92 | connector.setPort(port); 93 | return connector; 94 | } 95 | 96 | @Before 97 | public void beforeTest() { 98 | webClient.setThrowExceptionOnFailingStatusCode(true); 99 | } 100 | 101 | public void pauseServer(boolean paused) { 102 | if (server != null) server.pause(paused); 103 | } 104 | 105 | public static class PauseableServer extends Server { 106 | public synchronized void pause(boolean paused) { 107 | try { 108 | if (paused) for (Connector connector : getConnectors()) 109 | connector.stop(); 110 | else for (Connector connector : getConnectors()) 111 | connector.start(); 112 | } catch (Exception e) { 113 | } 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/org/meri/simpleshirosecuredapplication/util/AnalyzeKeystore.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.util; 2 | 3 | import java.io.FileInputStream; 4 | import java.io.IOException; 5 | import java.math.BigInteger; 6 | import java.security.KeyStore; 7 | import java.security.cert.X509Certificate; 8 | import java.util.Enumeration; 9 | 10 | import org.apache.xerces.impl.dv.util.Base64; 11 | 12 | /** 13 | * Utility class. Investigates keystore file and prints all certificates along with authority names and serial numbers. 14 | */ 15 | public class AnalyzeKeystore { 16 | 17 | public static void main(String[] args) { 18 | AnalyzeKeystore instance = new AnalyzeKeystore(); 19 | instance.certificateOK("src/main/resources/truststore", "secret"); 20 | } 21 | 22 | private void certificateOK(String truststore, String password) { 23 | FileInputStream stream = null; 24 | 25 | try { 26 | stream = new FileInputStream(truststore); 27 | KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); 28 | keyStore.load(stream, password.toCharArray()); 29 | 30 | Enumeration aliases = keyStore.aliases(); 31 | while (aliases.hasMoreElements()) { 32 | String alias = aliases.nextElement(); 33 | X509Certificate certificate = (X509Certificate) keyStore.getCertificate(alias); 34 | 35 | BigInteger serialNumber = certificate.getSerialNumber(); 36 | String issuerName = certificate.getIssuerDN().getName(); 37 | String base64Serial = Base64.encode(serialNumber.toByteArray()); 38 | 39 | System.out.println(alias + ": " + base64Serial + "|XX|" + issuerName); 40 | } 41 | 42 | } catch (Exception ex) { 43 | ex.printStackTrace(); 44 | } finally { 45 | try { 46 | stream.close(); 47 | } catch (IOException e) { 48 | } 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/org/meri/simpleshirosecuredapplication/util/HashPassword.java: -------------------------------------------------------------------------------- 1 | package org.meri.simpleshirosecuredapplication.util; 2 | 3 | import org.apache.shiro.crypto.hash.Sha256Hash; 4 | import org.apache.shiro.util.SimpleByteSource; 5 | 6 | public class HashPassword { 7 | 8 | /** 9 | * @param args 10 | */ 11 | public static void main(String[] args) { 12 | simpleHash("heslo"); 13 | simpleSaltedHash("administrator", "heslo"); 14 | simpleSaltedHash("friendlyrepairman", "heslo"); 15 | simpleSaltedHash("unfriendlyrepairman", "heslo"); 16 | simpleSaltedHash("mathematician", "heslo"); 17 | simpleSaltedHash("physicien", "heslo"); 18 | simpleSaltedHash("productsales", "heslo"); 19 | simpleSaltedHash("servicessales", "heslo"); 20 | 21 | } 22 | 23 | private static String simpleHash(String password) { 24 | Sha256Hash sha256Hash = new Sha256Hash(password); 25 | String result = sha256Hash.toHex(); 26 | 27 | System.out.println("Simple hash: " + result); 28 | return result; 29 | } 30 | 31 | private static String simpleSaltedHash(String username, String password) { 32 | Sha256Hash sha256Hash = new Sha256Hash(password, (new SimpleByteSource("random_salt_value_" + username)).getBytes()); 33 | String result = sha256Hash.toHex(); 34 | 35 | System.out.println(username + " simple salted hash: " + result); 36 | return result; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/test/resources/clients/administrator/administrator.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/administrator/administrator.cer -------------------------------------------------------------------------------- /src/test/resources/clients/administrator/administrator.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/administrator/administrator.jks -------------------------------------------------------------------------------- /src/test/resources/clients/administrator/administrator.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/administrator/administrator.p12 -------------------------------------------------------------------------------- /src/test/resources/clients/friendlyrepaiman/friendlyrepairman.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/friendlyrepaiman/friendlyrepairman.cer -------------------------------------------------------------------------------- /src/test/resources/clients/friendlyrepaiman/friendlyrepairman.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/friendlyrepaiman/friendlyrepairman.jks -------------------------------------------------------------------------------- /src/test/resources/clients/friendlyrepaiman/friendlyrepairman.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/friendlyrepaiman/friendlyrepairman.p12 -------------------------------------------------------------------------------- /src/test/resources/clients/mathematician/mathematician.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/mathematician/mathematician.cer -------------------------------------------------------------------------------- /src/test/resources/clients/mathematician/mathematician.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/mathematician/mathematician.jks -------------------------------------------------------------------------------- /src/test/resources/clients/mathematician/mathematician.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/mathematician/mathematician.p12 -------------------------------------------------------------------------------- /src/test/resources/clients/physicien/physicien.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/physicien/physicien.cer -------------------------------------------------------------------------------- /src/test/resources/clients/physicien/physicien.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/physicien/physicien.jks -------------------------------------------------------------------------------- /src/test/resources/clients/physicien/physicien.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/physicien/physicien.p12 -------------------------------------------------------------------------------- /src/test/resources/clients/productsales/productsales.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/productsales/productsales.cer -------------------------------------------------------------------------------- /src/test/resources/clients/productsales/productsales.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/productsales/productsales.jks -------------------------------------------------------------------------------- /src/test/resources/clients/productsales/productsales.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/productsales/productsales.p12 -------------------------------------------------------------------------------- /src/test/resources/clients/servicessales/servicessales.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/servicessales/servicessales.cer -------------------------------------------------------------------------------- /src/test/resources/clients/servicessales/servicessales.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/servicessales/servicessales.jks -------------------------------------------------------------------------------- /src/test/resources/clients/servicessales/servicessales.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/servicessales/servicessales.p12 -------------------------------------------------------------------------------- /src/test/resources/clients/unfriendlyrepaiman/unfriendlyrepairman.cer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/unfriendlyrepaiman/unfriendlyrepairman.cer -------------------------------------------------------------------------------- /src/test/resources/clients/unfriendlyrepaiman/unfriendlyrepairman.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/unfriendlyrepaiman/unfriendlyrepairman.jks -------------------------------------------------------------------------------- /src/test/resources/clients/unfriendlyrepaiman/unfriendlyrepairman.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/clients/unfriendlyrepaiman/unfriendlyrepairman.p12 -------------------------------------------------------------------------------- /src/test/resources/keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/src/test/resources/keystore -------------------------------------------------------------------------------- /target/test-classes/keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SomMeri/SimpleShiroSecuredApplication/778f37b22fb023873a13976860e3692c55dce424/target/test-classes/keystore --------------------------------------------------------------------------------