Password authentication

This commit is contained in:
karl 2019-12-10 14:50:11 +01:00
parent 5e8529e89e
commit c4ea40899a
20 changed files with 238 additions and 4 deletions

View File

@ -17,6 +17,7 @@
<element id="archive" name="MoviesCommon-1.0.0-SNAPSHOT.jar"> <element id="archive" name="MoviesCommon-1.0.0-SNAPSHOT.jar">
<element id="module-output" name="MoviesCommon" /> <element id="module-output" name="MoviesCommon" />
</element> </element>
<element id="library" level="project" name="Maven: commons-codec:commons-codec:1.13" />
</element> </element>
</element> </element>
<element id="directory" name="META-INF"> <element id="directory" name="META-INF">

11
.idea/dataSources.xml generated Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="db_movies@localhost" uuid="7782f0d2-6685-4308-9359-efdc01dfeba0">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://localhost:3306/db_movies</jdbc-url>
</data-source>
</component>
</project>

View File

@ -85,6 +85,38 @@
</plugins> </plugins>
</pluginManagement> </pluginManagement>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<outputDirectory>${project.build.directory}</outputDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**.*</include>
</includes>
<targetPath>classes</targetPath>
</resource>
<resource>
<directory>src/jaxws</directory>
<includes>
<include>authfile</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>copy-resources</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>com.helger.maven</groupId> <groupId>com.helger.maven</groupId>
<artifactId>jaxws-maven-plugin</artifactId> <artifactId>jaxws-maven-plugin</artifactId>
@ -95,6 +127,7 @@
</wsdlUrls> </wsdlUrls>
<packageName>at.technikumwien.movies.generated</packageName> <packageName>at.technikumwien.movies.generated</packageName>
<sourceDestDir>${project.build.directory}/generated</sourceDestDir> <sourceDestDir>${project.build.directory}/generated</sourceDestDir>
<xauthFile>${project.build.directory}/authfile</xauthFile>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>
@ -114,4 +147,9 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<properties>
<movies.user>moviesuser</movies.user>
<movies.password>topsecret</movies.password>
</properties>
</project> </project>

View File

@ -0,0 +1 @@
http://${movies.user}:${movies.password}@localhost:8080/movieservice/MoviesWebService?wsdl

View File

@ -7,7 +7,9 @@ import java.util.List;
public class MovieResourceClient { public class MovieResourceClient {
public static void main(String[] args) { public static void main(String[] args) {
var target = ClientBuilder.newClient().target("http://localhost:8080/movieservice/resources/movie"); var target = ClientBuilder.newClient()
.register(new RequestFilter("moviesuser", "topsecret"))
.target("http://localhost:8080/movieservice/resources/movie");
List<Movie> allMovies = target List<Movie> allMovies = target
.request(MediaType.APPLICATION_XML) .request(MediaType.APPLICATION_XML)

View File

@ -10,6 +10,8 @@ import java.util.List;
public class MoviesWebServiceClient { public class MoviesWebServiceClient {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
PasswordAuthenticator.install("moviesuser", "topsecret");
MoviesWebService_Service service = new MoviesWebService_Service(); MoviesWebService_Service service = new MoviesWebService_Service();
MoviesWebService port = service.getMoviesWebServicePort(); MoviesWebService port = service.getMoviesWebServicePort();

View File

@ -0,0 +1,19 @@
package at.technikumwien.movies;
import java.net.Authenticator;
import java.net.InetAddress;
import java.net.PasswordAuthentication;
import java.net.URL;
public class PasswordAuthenticator {
private PasswordAuthenticator() {}
public static void install(String username, String password) {
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password.toCharArray());
}
});
}
}

View File

@ -0,0 +1,25 @@
package at.technikumwien.movies;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientRequestFilter;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class RequestFilter implements ClientRequestFilter {
private String username;
private String password;
public RequestFilter(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public void filter(ClientRequestContext request) throws IOException {
var token = username + ":" + password;
var basicAuthentication = "Basic " + DatatypeConverter.printBase64Binary(token.getBytes(StandardCharsets.UTF_8));
request.getHeaders().add("Authorization", basicAuthentication);
}
}

View File

@ -13,7 +13,8 @@
let options = { let options = {
mode: "cors", mode: "cors",
headers: { headers: {
"Accept": "application/json" "Accept": "application/json",
"Authorization": "Basic " + btoa("moviesuser:topsecret")
} }
}; };

View File

@ -4,6 +4,7 @@
<facet type="web" name="Web"> <facet type="web" name="Web">
<configuration> <configuration>
<descriptors> <descriptors>
<deploymentDescriptor name="jboss-web.xml" url="file://$MODULE_DIR$/src/main/webapp/WEB-INF/jboss-web.xml" />
<deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/src/main/webapp/WEB-INF/web.xml" /> <deploymentDescriptor name="web.xml" url="file://$MODULE_DIR$/src/main/webapp/WEB-INF/web.xml" />
</descriptors> </descriptors>
<webroots> <webroots>
@ -75,5 +76,6 @@
<orderEntry type="library" scope="PROVIDED" name="Maven: jakarta.xml.soap:jakarta.xml.soap-api:1.4.1" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Maven: jakarta.xml.soap:jakarta.xml.soap-api:1.4.1" level="project" />
<orderEntry type="library" scope="PROVIDED" name="Maven: jakarta.jws:jakarta.jws-api:1.1.1" level="project" /> <orderEntry type="library" scope="PROVIDED" name="Maven: jakarta.jws:jakarta.jws-api:1.1.1" level="project" />
<orderEntry type="module" module-name="MoviesCommon" /> <orderEntry type="module" module-name="MoviesCommon" />
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.13" level="project" />
</component> </component>
</module> </module>

View File

@ -45,6 +45,12 @@
<artifactId>MoviesCommon</artifactId> <artifactId>MoviesCommon</artifactId>
<version>1.0.0-SNAPSHOT</version> <version>1.0.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.13</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -1,5 +1,6 @@
package at.technikumwien.movies; package at.technikumwien.movies;
import javax.annotation.security.RolesAllowed;
import javax.inject.Inject; import javax.inject.Inject;
import javax.ws.rs.*; import javax.ws.rs.*;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
@ -10,6 +11,7 @@ import java.net.URI;
import java.util.List; import java.util.List;
@Path("/movie") @Path("/movie")
@RolesAllowed("MoviesUserRole")
public class MovieResource { public class MovieResource {
@Inject @Inject
private MoviesService moviesService; private MoviesService moviesService;

View File

@ -0,0 +1,24 @@
package at.technikumwien.movies;
import javax.annotation.security.DeclareRoles;
import javax.enterprise.context.ApplicationScoped;
import javax.security.enterprise.authentication.mechanism.http.BasicAuthenticationMechanismDefinition;
import javax.security.enterprise.identitystore.DatabaseIdentityStoreDefinition;
@BasicAuthenticationMechanismDefinition(realmName = "MoviesWebApp")
@DeclareRoles({
"MoviesAdminRole",
"MoviesUerRole"
})
@DatabaseIdentityStoreDefinition(
dataSourceLookup = "java:jboss/datasources/MoviesDS",
callerQuery = "SELECT password FROM t_user WHERE username = ?",
groupsQuery = "SELECT r.rolename FROM t_user u " +
"INNER JOIN t_user_role ur ON u.id = ur.userid " +
"INNER JOIN t_role r ON ur.roleid = r.id " +
"WHERE u.username = ?",
hashAlgorithm = SH2HexPasswordHash.class
)
@ApplicationScoped
public class MovieSecurityConfig {
}

View File

@ -1,16 +1,20 @@
package at.technikumwien.movies; package at.technikumwien.movies;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.*; import javax.ejb.*;
import javax.inject.Inject;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException; import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContext;
import javax.security.enterprise.SecurityContext;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
@Stateless @Stateless
@TransactionManagement(value=TransactionManagementType.CONTAINER) @TransactionManagement(value=TransactionManagementType.CONTAINER)
@RolesAllowed("MoviesUserRole")
public class MoviesService { public class MoviesService {
private static final Logger LOGGER = Logger.getLogger(MoviesService.class.getName()); private static final Logger LOGGER = Logger.getLogger(MoviesService.class.getName());
@ -20,6 +24,9 @@ public class MoviesService {
@PersistenceContext(unitName = "MoviesPU") @PersistenceContext(unitName = "MoviesPU")
private EntityManager em; private EntityManager em;
@Inject
private SecurityContext securityContext;
public Movie findById(long id) { public Movie findById(long id) {
LOGGER.info("findById() >> id=" + id); LOGGER.info("findById() >> id=" + id);
@ -42,6 +49,9 @@ public class MoviesService {
public List<Movie> findAll() { public List<Movie> findAll() {
LOGGER.info("findAll()"); LOGGER.info("findAll()");
LOGGER.info("> principal: " + (securityContext.getCallerPrincipal() != null ? securityContext.getCallerPrincipal().getName() : null));
LOGGER.info("> role('MoviesUserRole'): " + (securityContext.isCallerInRole("MoviesUserRole")));
return em.createNamedQuery("Movies.selectAll", Movie.class) //JPQL -> java persistence query language return em.createNamedQuery("Movies.selectAll", Movie.class) //JPQL -> java persistence query language
.getResultList(); .getResultList();
} }

View File

@ -2,6 +2,8 @@ package at.technikumwien.movies;
import javax.inject.Inject; import javax.inject.Inject;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.annotation.HttpConstraint;
import javax.servlet.annotation.ServletSecurity;
import javax.servlet.annotation.WebServlet; import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -10,6 +12,9 @@ import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
@WebServlet(urlPatterns = "movieserviceservlet") @WebServlet(urlPatterns = "movieserviceservlet")
@ServletSecurity(
@HttpConstraint(rolesAllowed = "MoviesUserRole")
)
public class MoviesServlet extends HttpServlet { public class MoviesServlet extends HttpServlet {
@Inject @Inject
private MoviesService moviesService; private MoviesService moviesService;

View File

@ -0,0 +1,18 @@
package at.technikumwien.movies;
import org.apache.commons.codec.digest.DigestUtils;
import javax.security.enterprise.identitystore.PasswordHash;
import java.util.Objects;
public class SH2HexPasswordHash implements PasswordHash {
@Override
public String generate(char[] chars) {
return DigestUtils.sha512Hex(new String(chars));
}
@Override
public boolean verify(char[] chars, String s) {
return Objects.equals(generate(chars), s);
}
}

View File

@ -7,9 +7,12 @@
<jta-data-source>java:jboss/datasources/MoviesDS</jta-data-source> <jta-data-source>java:jboss/datasources/MoviesDS</jta-data-source>
<jar-file>lib/MoviesCommon-1.0.0-SNAPSHOT.jar</jar-file> <jar-file>lib/MoviesCommon-1.0.0-SNAPSHOT.jar</jar-file>
<properties> <properties>
<property name="javax.persistence.sql-load-script-source" value="META-INF/sql/security-data.sql"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL8Dialect"/>
<property name="hibernate.show_sql" value="true" /> <property name="hibernate.show_sql" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.hbm2ddl.import_files_sql_extractor"
value="org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor"/>
<property name="hibernate.hbm2ddl.auto" value="update"/> <!-- value="create-drop" for loading security-data.sql -->
</properties> </properties>
</persistence-unit> </persistence-unit>
</persistence> </persistence>

View File

@ -0,0 +1,31 @@
DROP TABLE IF EXISTS t_user_role;
DROP TABLE IF EXISTS t_user;
DROP TABLE IF EXISTS t_role;
CREATE TABLE t_user (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(25) NOT NULL,
password VARCHAR(128) NOT NULL
);
CREATE TABLE t_role (
id INT AUTO_INCREMENT PRIMARY KEY,
rolename VARCHAR(25) NOT NULL
);
CREATE TABLE t_user_role (
id INT AUTO_INCREMENT PRIMARY KEY,
userid INT NOT NULL,
roleid INT NOT NULL,
FOREIGN KEY (userid) REFERENCES t_user(id),
FOREIGN KEY (roleid) REFERENCES t_role(id)
);
INSERT INTO t_user (id, username, password) VALUES (1, 'moviesadmin', SHA2('topsecret', 512));
INSERT INTO t_user (id, username, password) VALUES (2, 'moviesuser', SHA2('topsecret', 512));
INSERT INTO t_role (id, rolename) VALUES (1, 'MoviesAdminRole');
INSERT INTO t_role (id, rolename) VALUES (2, 'MoviesUserRole');
INSERT INTO t_user_role (id, userid, roleid) VALUES (1, 1, 1);
INSERT INTO t_user_role (id, userid, roleid) VALUES (2, 2, 2);

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-web_14_0.xsd"
version="14.0">
<security-domain>jaspitest</security-domain>
</jboss-web>

View File

@ -17,4 +17,30 @@
<servlet-name>Faces Servlet</servlet-name> <servlet-name>Faces Servlet</servlet-name>
<url-pattern>*.xhtml</url-pattern> <url-pattern>*.xhtml</url-pattern>
</servlet-mapping> </servlet-mapping>
<context-param>
<param-name>resteasy.role.based.security</param-name>
<param-value>true</param-value>
</context-param>
<security-constraint>
<web-resource-collection>
<web-resource-name>MoviesWebApp</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method-omission>OPTIONS</http-method-omission>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>NONE</transport-guarantee> <!-- CONFIDENTIAL -> only HTTPS possible -->
</user-data-constraint>
</security-constraint>
<security-role>
<role-name>MoviesAdminRole</role-name>
</security-role>
<security-role>
<role-name>MoviesUserRole</role-name>
</security-role>
</web-app> </web-app>