November 20, 2018

Setting Global HTTP Session Timeout in JBoss EAP 6 and 7

JBoss EAP 6.x

Edit standalone.xml.

<subsystem xmlns="urn:jboss:domain:web:2.2" default-virtual-server="default-host" native="false" default-session-timeout="15">
...
</subsystem>

JBoss EAP 7.x

Edit standalone.xml.

<subsystem xmlns="urn:jboss:domain:undertow:4.0">
...
    <servlet-container name="default" default-session-timeout="15">
...
    </servlet-container>
...
</subsystem>

Note

You can set this in WEB-INF/web.xml also, but this is better to set globally.

Set HttpOnly and secure for Cookies in JBoss EAP 6 and 7

What is Cookie HttpOnly and secure?

Example

Set-Cookie: a=b; HttpOnly; SameSite=strict; secure

HttpOnly = No JavaScript

SameSite = no cross-origin cookie sharing

secure = SSL only

SameSite is not yet supported in all browser, see https://caniuse.com/#feat=same-site-cookie-attribute

JBoss EAP 6.x

Not supported to set globally. Best effort is deploy Servlet 3.0 web-fragment.xml and deploy overlay which is messy.

Next best in local application WEB-INF/web.xml.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
    version="3.0">
...
    <session-config>
        <!-- Session timeout after X MINUTES after no user interaction. -->
        <session-timeout>15</session-timeout>
        <cookie-config>
            <http-only>true</http-only>
            <secure>true</secure>
        </cookie-config>
        <tracking-mode>COOKIE</tracking-mode>
    </session-config>
</web-app>

JBoss EAP 7.x

Edit standalone.xml

<subsystem xmlns="urn:jboss:domain:undertow:4.0">
...
    <servlet-container name="default">
...
        <session-cookie http-only="true" secure="true" />
    </servlet-container>
...
</subsystem>

How to Enable RequestDump in JBoss EAP 6 and 7

What is RequestDump?

It is a great debugging tool, that prints HTTP request and response in jboss server.log.

JBoss EAP EAP 6.1.0 or later

Edit standalone.xml.

<subsystem xmlns="urn:jboss:domain:web:2.2" default-virtual-server="default-host" native="false">
    ...
    <virtual-server name="default-host" enable-welcome-root="true">
        ...
    </virtual-server>
    <valve name="RequestDumperValve" module="org.jboss.as.web" class-name="org.apache.catalina.valves.RequestDumperValve"/>
</subsystem>

JBoss EAP EAP 7.x

Edit standalone.xml.

<subsystem xmlns="urn:jboss:domain:undertow:4.0">
    ...
    <server name="default-server">
        ...
    </server>
    ...
    <filters>
        ...
        <expression-filter name="requestDumperExpression" expression="dump-request"/>
    </filters>
</subsystem>

November 17, 2018

Spring Security

What is it?

A security framework, that competes with a framework that are built in Java EE and theirs servers. And is a mandatory requirement when running Spring Boot.

Prerequisite

  • Will use Spring Tools (STS) version 3 and not version 4, since latest version lacks the built in Tomcat (actually it is a modified Spring Tomcat, Pivotal TC Server).
  • Will use my previous blog example of Getting Started with Spring MVC .
  • Will use jetty maven plugin (org.eclipse.jetty:jetty-maven-plugin) to test and run the application via 'mvn jetty:run'.

Setup

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
    <version>${springframework.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
    <version>${springframework.version}</version>
</dependency>

Add to the src/main/webapp/WEB-INF/web.xml.

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

and /WEB-INF/spring/security-context.xml to your contextConfigLocation. The complete web.xml looks now with Spring MVC:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1">

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/application-context.xml
            /WEB-INF/spring/security-context.xml
        </param-value>
    </context-param>

    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

And now an example /WEB-INF/spring/security-context.xml which protectes all HTTP with the role 'ROLE_USER' and in-memory user 'foo' with password 'changeit' and role 'ROLE_USER'.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

    <security:http auto-config="true" use-expressions="false">
        <security:intercept-url pattern="/**" access="ROLE_USER" />
    </security:http>

    <security:authentication-manager>
        <security:authentication-provider>
            <security:password-encoder ref="passwordEncoder" />
            <security:user-service> 
                <security:user name="foo" password="changeit" authorities="ROLE_USER" /> 
            </security:user-service>
        </security:authentication-provider>
    </security:authentication-manager>

    <!-- java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for 
        the id "null" -->
    <bean id="passwordEncoder"
        class="org.springframework.security.crypto.password.NoOpPasswordEncoder"
        factory-method="getInstance" />
</beans>

Architectual Overview

Authorization

Either via XML (or Java which is the requirement for Spring Boot).

<security:http auto-config="true" use-expressions="false">
    <security:intercept-url pattern="/**" access="ROLE_USER" />
</security:http>

What is important is that the rule with highest clearance must come first and then in decreased clearance. The reason for this is because the first rule that passes, will exit the process. Example of a misconfiguration.

<security:http auto-config="true" use-expressions="false">
    <security:intercept-url pattern="/**" access="ROLE_ANONYMOUS" />
    <security:intercept-url pattern="/topsecret/*" access="ROLE_TOP_SECRET" />
</security:http>

This configuration will lead to that everyone will be granted access to '/topsecret/*', since the first rule will be successfull and will overlay any further more restricted authorization rules.

Another way to enforce authoriation is via class or method annotation @org.springframework.security.access.annotation.Secured. Example:

@Secured({ "ROLE_USER" })
public void create(Contact contact) {...}

Authentication

Basic

/WEB-INF/spring/security-context.xml

    <security:http auto-config="true" use-expressions="false">
        <security:http-basic />
        ...
    </security:http>

Form

/WEB-INF/spring/security-context.xml

    <security:http auto-config="true" use-expressions="false">
        <security:form-login login-page="/login"
            login-processing-url="/login" default-target-url="/"
            always-use-default-target="true"
            authentication-failure-url="/login?error=true" />
        ...
    </security:http>

View in JSP.

<form action=${loginVar} method="POST">
Username: <input type="text" name="username" /></br>
Password: <input type="password" name="password" /></br>
<input type="submit" value="Login" />
</form>

Logout

View in JSP.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
...
<a href="<c:url value="/logout" />">Logout</a>
...

/WEB-INF/spring/security-context.xml

    <security:http auto-config="true" use-expressions="false">
        ...
        <security:logout logout-success-url="/login?logout=true"/>
        ...
    </security:http>

JDBC Module

Add maven dependency

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.1.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.185</version>
        </dependency>

WEB-INF/spring/application-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <tx:annotation-driven />

    <jdbc:embedded-database id="datasource" type="H2">
        <jdbc:script location="classpath:initdb.sql" />
    </jdbc:embedded-database>

    ...
</beans>

src/main/resources/initdb.sql

drop table users if exists;
drop table authorities if exists;

create table users(
    username varchar_ignorecase(50) not null primary key,
    password varchar_ignorecase(50) not null,
    enabled boolean not null
);

create table authorities (
    username varchar_ignorecase(50) not null,
    authority varchar_ignorecase(50) not null,
    constraint fk_authorities_users foreign key(username) references users(username)
);
create unique index ix_auth_username on authorities (username,authority);

insert into users(username, password, enabled) values ('foodb', 'changeit', true);
insert into authorities(username, authority) values ('foodb', 'ROLE_USER');

src/main/webapp/WEB-INF/spring/security-context.xml

    <security:authentication-manager>
        <security:authentication-provider>
            <security:password-encoder ref="passwordEncoder" />
            <security:jdbc-user-service data-source-ref="datasource" />
        </security:authentication-provider>
    </security:authentication-manager>

Advanced JDBC Module - Groups

src/main/webapp/WEB-INF/spring/security-context.xml

    <security:authentication-manager>
        <security:authentication-provider>
            <security:password-encoder ref="passwordEncoder" />
            <security:jdbc-user-service data-source-ref="datasource"
                group-authorities-by-username-query="select g.id, g.group_name, ga.authority
                    from groups g, group_members gm, group_authorities ga
                    where gm.username = ? and g.id = ga.group_id and g.id = gm.group_id" />
        </security:authentication-provider>
    </security:authentication-manager>

src/main/resources/initdb.sql add the Group Authorities tables

create table groups (
    id bigint generated by default as identity(start with 0) primary key,
    group_name varchar_ignorecase(50) not null
);

create table group_authorities (
    group_id bigint not null,
    authority varchar(50) not null,
    constraint fk_group_authorities_group foreign key(group_id) references groups(id)
);

create table group_members (
    id bigint generated by default as identity(start with 0) primary key,
    username varchar(50) not null,
    group_id bigint not null,
    constraint fk_group_members_group foreign key(group_id) references groups(id)
);

And remove the 'insert into authorities' and instead insert into group authorities tables.

insert into users(username, password, enabled) values ('foodb', 'changeit', true);
--insert into authorities(username, authority) values ('foodb', 'ROLE_USER');
insert into groups(id, group_name) values (1, 'Admin');
insert into group_authorities(group_id, authority) values (1, 'ROLE_USER');
insert into group_members(id, username, group_id) values (1, 'foodb', 1);

Hash Algorithm

https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/crypto/password/PasswordEncoder.html

LDAP Module

Will use The Apache Directory, which is written in Java.

Download Apache DS. Download Archive zip/tar.gz.

Apache Directory Studio. Download Archive zip/tar.gz.

Unzip both zip/tar.gz to your desktop.

$ cd /home/magnuskkarlsson/bin/apacheds-2.0.0.AM25/bin
$ ./apacheds.sh start
Using ADS_HOME:    /home/magnuskkarlsson/bin/apacheds-2.0.0.AM25
Using JAVA_HOME:   

Starting ApacheDS instance 'default'...

Now start your 'ApacheDirectoryStudio'. Goto view 'LDAP Servers', click new and add ApacheDS 2.0.0 server. Then stop your apacheds server.

$ ./apacheds.sh stop

Return to studio and right click on you ApacheDS server and choose 'Create a Connection' and right click again and choose 'Run'.

Now we need to add a new partition for your database. Switch to LDAP Servers and double click on your local ldap, in the main space click Partitions and click Add.

After editing click Save. Then stop you local ldap and then start. After restarted double click on Connection and you will see your new partition.

Now we are going to import our users. Right click on Root DSE and choose Import and LDIF Import.

src/test/resources/spring-security-ldap.ldif

dn: dc=magnuskkarlsson,dc=se
objectclass: top
objectclass: domain
objectclass: extensibleObject
dc: magnuskkarlsson

dn: ou=groups,dc=magnuskkarlsson,dc=se
objectclass: top
objectclass: organizationalUnit
ou: groups

dn: ou=people,dc=magnuskkarlsson,dc=se
objectclass: top
objectclass: organizationalUnit
ou: people


dn: uid=ben,ou=people,dc=magnuskkarlsson,dc=se
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Ben Alex
sn: Alex
uid: ben
userPassword: changeit

dn: uid=bob,ou=people,dc=magnuskkarlsson,dc=se
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Bob Hamilton
sn: Hamilton
uid: bob
userPassword: changeit


dn: cn=user,ou=groups,dc=magnuskkarlsson,dc=se
objectclass: top
objectclass: groupOfUniqueNames
cn: user
uniqueMember: uid=ben,ou=people,dc=magnuskkarlsson,dc=se
uniqueMember: uid=bob,ou=people,dc=magnuskkarlsson,dc=se

dn: cn=admin,ou=groups,dc=magnuskkarlsson,dc=se
objectclass: top
objectclass: groupOfUniqueNames
cn: admin
uniqueMember: uid=ben,ou=people,dc=magnuskkarlsson,dc=se

Now configure Spring Security LDAP, but first we need to add maven dependency.

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-ldap</artifactId>
            <version>${springframework.version}</version>
        </dependency>

src/main/webapp/WEB-INF/spring/security-context-ldap.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:security="http://www.springframework.org/schema/security"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">

    <security:http auto-config="true" use-expressions="false">
        <security:intercept-url pattern="/**" access="ROLE_USER" />
        <security:logout logout-success-url="/login" />
    </security:http>

    <security:authentication-manager>
        <security:ldap-authentication-provider server-ref="ldapServer" 
            user-dn-pattern="uid={0},ou=people" group-search-base="ou=groups">
        </security:ldap-authentication-provider>
    </security:authentication-manager>

    <security:ldap-server id="ldapServer" 
        url="ldap://localhost:10389/dc=magnuskkarlsson,dc=se" 
        manager-dn="uid=admin,ou=system" manager-password="secret" />
</beans>

Expression-Based Access Control/Spring Expression Language(SPEL)

First you need to declare SPEL bean then change 'use-expressions' to true. Then you can use your expression.

https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#el-access

    <bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" />

    <security:http auto-config="true" use-expressions="true">
        
        <security:intercept-url pattern="/**" access="hasRole('ROLE_USER') and hasIpAddress('192.168.1.0/24')" />
        ...
    </security:http>

Spring Security Taglib

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-taglibs</artifactId>
            <version>${springframework.version}</version>
        </dependency>
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#taglibs

Example in your JSP page.

<sec:authorize access="isAuthenticated()">
    Welcome 
    <sec:authentication property="principal.firstName"/> 
    <sec:authentication! property="principal.lastName"/>
</sec:authorize>
<sec:authorize access="hasRole('ROLE_ADMIN')">
    TOP SECRET
</sec:authorize>

Access Control List, ACL

The ACL is used to grant access to specific person and/or rule to specfic instances of a object.

Example. You have reports and some reports (db with primary key e.g. 1 and 3) should only be accessed by user (db primary key user e.g. 1, 5). That can you enforce with ACL.

Or specific reports should only be granted read access, that can also be enforced with ACL.

The downside with ACL is management of this finegrained access control.

https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#dbschema-acl

https://www.baeldung.com/spring-security-acl

CSRF

Use spring security form taglib

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

<form:form ...>
</form>

Eitherwise you need to manually add it.

<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>

<form ...>
    <sec:csrfInput />
</form>

Java Configuration Part

So far I have configured Spring Security through XML, but today in the spring community is moving to writing java code instead. My opinion is that XML is more concisely and more easy to overview, but XML has a bad reputation in the spring community.

Start class.

package se.magnuskkarlsson.example.springmvc;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    public SecurityWebApplicationInitializer() {
        super(SecurityConfig.class);
    }
}

Then the actual authentication and authorization code.

package se.magnuskkarlsson.example.springmvc;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportResource;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
@ImportResource("/WEB-INF/spring/application-context.xml")
@ComponentScan("se.magnuskkarlsson.example")
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("foojava").password("changeit").roles("USER");
    }
}

November 13, 2018

Getting Started with Spring MVC

The OLD XML Configuration

pom.xml. The jstl and servlet-api is only need if you use JSTL in your JSP pages.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>
    <groupId>se.magnuskkarlsson.examples</groupId>
    <artifactId>example-springmvc-xml</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.build.outputEncoding>UTF-8</project.build.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- embedded Jetty server, for testing -->
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.2.11.v20150529</version>
                <configuration>
                    <scanIntervalSeconds>10</scanIntervalSeconds>
                    <webApp>
                        <contextPath>/</contextPath>
                    </webApp>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

web.xml. The old school, but is good to know, since there are several old references to the XML way.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1">

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/application-context.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value></param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

/WEB-INF/spring/application-context.xmlIn Spring you can split the Spring XML configuration into several files, but I tend to think that using several configuration files, will only make you wander lost in configuration, so I prefer one configuration file.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="se.magnuskkarlsson.example.springmvc.controller" />

    <mvc:annotation-driven />

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/" />
        <property name="suffix" value=".jsp" />
    </bean>
</beans>

Now the actual "app". Spring MVC design is much like Struts with one Servlet that dispatch request. HelloController

package se.magnuskkarlsson.example.springmvc.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
public class HelloController {

    @RequestMapping(value = "/hello", method = RequestMethod.GET)
    public String hello(@RequestParam(value = "name", required = false, defaultValue = "World") String name,
            Model model) {

        model.addAttribute("name", name);
        return "hello";
    }
}

And your app view - WEB-INF/view/hello.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello Spring MVC</title>
</head>
<body>
    <h2>Hello Java ${name}!</h2>
</body>
</html>

And finally lets test it by running 'mvn jetty:run' and open http://localhost:8080/hello?name=foo.

$ mvn clean install; mvn jetty:run

The NEW Java Configuration

Now we delete web.xml and /WEB-INF/spring/application-context.xml. And manually write code that do the same thing.

package se.magnuskkarlsson.example.springmvc;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.context.support.GenericWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class MainWebAppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) throws ServletException {
        // Create the 'root' Spring application context
        AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
        rootContext.scan("se.magnuskkarlsson");

        // Manage the lifecycle of the root application context
        container.addListener(new ContextLoaderListener(rootContext));

        // Register and map the dispatcher servlet
        ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher",
                new DispatcherServlet(new GenericWebApplicationContext()));
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }
}
package se.magnuskkarlsson.example.springmvc;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@EnableWebMvc
@Configuration
@ComponentScan(basePackages = { "se.magnuskkarlsson.example.springmvc.controller" })
public class AppConfig implements WebMvcConfigurer {

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver bean = new InternalResourceViewResolver();
        bean.setPrefix("/WEB-INF/view/");
        bean.setSuffix(".jsp");
        return bean;
    }
}

November 11, 2018

TestNG

Comparison with JUnit

TestNG is a direct competitor with JUnit. But that may change with JUnit 5.

The main advantage of TestNG compared with JUnit 4, is that TestNG has build in support of Data Provider and Parallel support.

Below follows a summary of feature in TestNG.

Setup

Maven

<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>6.14.3</version>
    <scope>test</scope>
</dependency>

Eclipse

Open Eclipse and click on Help menu and Install New Software. Enter http://beust.com/eclipse

And then next and finish plugin installation.

TestNG Annotations

    @BeforeSuite
    @AfterSuite

    @BeforeTest
    @AfterTest

    @BeforeClass
    @AfterClass

    @BeforeMethod
    @AfterMethod

    @Test

    // order, but each test method should be independent
    @Test(priority=0)

Grouping Related Test Methods

    // group test method, so they are executed logical together
    @Test(groups = "cars")

Used for test suite, annotate with attribute group and call group in test suite xml.

TestNG Assert

    org.testng.Assert
        assertTrue
        assertFalse
        fail
        assertEquals
        assertNotNull
        assertNull
        assertSame
        assertNotSame

Running a Test Suite

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="tests">
    <test name="backend">
        <classes>
            <class name="se.magnuskkarksson.testng.SimpleTest" />
        </classes>
    </test>
</suite>

To run, right click on xml file and choose TestNG Suite

See also for more example of test suite. http://websystique.com/java/testing/testng-suites-example/

For Maven Surefire Plugin see http://maven.apache.org/surefire/maven-surefire-plugin/examples/testng.html

Dependent Test Methods

@Test(dependsOnMethods = {"test1"})
public void test1() throws Exception { }

@Test(dependsOnMethods = {"test2"})
public void test2() throws Exception { }

Disable and Timeout Test

@Test(enabled = false)
public void testEnabled() throws Exception { }

// The maximum number of milliseconds
@Test(timeOut = 1000)
public void testTimeConstraint() throws Exception { }

Providing Test Data from XML file

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="tests">
    <test name="backend">
        <parameter name="param1" value="value1" />
        <parameter name="param2" value="value2" />
        <parameter name="param3" value="value3" />
        <classes>
            <class name="se.magnuskkarksson.testng.SimpleTest" />
        </classes>
    </test>
</suite>
@BeforeClass
@Parameters({ "param1", "param2" })
public void beforeClass(String param1, String param2) throws Exception { }

@Test
@Parameters({ "param1" })
public void test(String param1) throws Exception { }

Running Tests in Parallel

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="tests" parallel="tests" thread-count="2">
 
    <test name="front-end">
        <packages>
            <package name="se.magnuskkarlsson.testng.frontend.*" />
        </packages>
    </test>
    <test name="back-end">
        <packages>
            <package name="se.magnuskkarlsson.testng.backend.*" />
        </packages>
    </test>
</suite>

Data Providers

package se.magnuskkarksson.testng;

import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

public class DataProviderTest {

    @Test(dataProvider = "testData")
    public void test(String param1, String param2) throws Exception {
        System.out.println("param1: " + param1 + ", param2: " + param2);
    }

    @DataProvider(name = "testData")
    public Object[][] getTestData() {
        return new Object[][] { //
                { "foo1", "bar1" }, //
                { "foo2", "bar2" } //
        };
    }
}

To reuse testdata move data provider to seperate class.

package se.magnuskkarksson.testng;

import org.testng.annotations.DataProvider;

public class TestData {

    @DataProvider(name = "testData")
    public Object[][] getTestData() {
        return new Object[][] { //
                { "foo1", "bar1" }, //
                { "foo2", "bar2" } //
        };
    }
}

Then modify @Test annotation attribute

package se.magnuskkarksson.testng;

import org.testng.annotations.Test;

public class DataProviderTest {

    @Test(dataProvider = "testData", dataProviderClass = TestData.class)
    public void test(String param1, String param2) throws Exception {
        System.out.println("param1: " + param1 + ", param2: " + param2);
    }
}

TestNG Listeners

org.testng.ITestResult 
org.testng.IInvokedMethodListener
org.testng.ITestListener
org.testng.ISuiteListener

TestNG Reporter Logs and HTML Reports

To create HTML TestNG report add org.testng.Reporter#log() logging to your unit test file. E.g.

@Test
public void aFastTest() {
    Assert.assertTrue(true);
    Reporter.log("Logging.....");
}

Then create test suite xml file and run test suite. Then will a test-output directory be created with a HTML report in it.

TestNG Selenium

Install chrome web browser. On Fedora 28 'yum install google-chrome-stable'

Download chrome webdriver from selenium web page - https://www.seleniumhq.org/download/. Which links to https://chromedriver.storage.googleapis.com/index.html?path=2.43/.

Download zip file and unzip somewhere on disk.

Add selenium for java maven dependency.

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.141.5</version>
    <scope>test</scope>
</dependency>

Write test unit test.

package se.magnuskkarksson.testng;

import org.openqa.selenium.WebDriver;
import org.testng.Assert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.openqa.selenium.chrome.ChromeDriver;

public class SeleniumParallelTest {

    private String baseUrl;

    private WebDriver driver;

    @BeforeClass
    public void beforeClass() throws Exception {
        baseUrl = "https://www.google.com/";
        System.setProperty("webdriver.chrome.driver",
                "/home/magnuskkarlsson/bin/selenium/chromedriver_linux64/chromedriver");
        driver = new ChromeDriver();
        driver.get(baseUrl);
    }

    @AfterClass
    public void afterClass() throws Exception {
        driver.quit();
    }

    @Test
    public void test() throws Exception {
        String title = driver.getTitle();
        System.out.println("Title: " + title);
        Assert.assertEquals(title, "Google");
    }
}

November 2, 2018

Oracle Code One 2018 Keynote by Mark Reinhold Chief Architect

Entire Keynote

https://www.youtube.com/watch?v=RFF2SfPMfpk

Oracle JDK for Windows is Not Free Anymore

Oracle JDK for Windows is Not Free Anymore

Subscription
$ 25.00 per month per processer
$ 2.50 per month and user
https://www.oracle.com/java/java-se-subscription.html

Who is contributing to OpenJDK?

https://blogs.oracle.com/java-platform-group/building-jdk-11-together

Java Champions

https://github.com/aalmiray/java-champions

Packages Removed in Java 11

Before Java 11

After Java 11

Reference

  1. JEP 261: Module System (Jigsaw)
  2. JEP 320: Remove the Java EE and CORBA Modules

Removed in Java 9

java.util.jar.Pack200.Packer.addPropertyChangeListener(...)
java.util.jar.Pack200.Packer.removePropertyChangeListener(...)
java.util.jar.Pack200.Unpacker.addPropertyChangeListener(...)
java.util.jar.Pack200.Unpacker.removePropertyChangeListener(...)
java.util.logging.LogManager.addPropertyChangeListener(...)
java.util.logging.LogManager.removePropertyChangeListener(...)

Removed in Java 10

// Removed in Java 10
java.lang.Runtime.getLocalizedInputStream(...)
java.lang.Runtime.getLocalizedOutputStream(...)
java.lang.SecurityManager.classDepth(...)
java.lang.SecurityManager.classLoaderDepth()
java.lang.SecurityManager.currentClassLoader()
java.lang.SecurityManager.currentLoadedClass()
java.lang.SecurityManager.getInCheck()
java.lang.SecurityManager.inClass(...)
java.lang.SecurityManager.inClassLoader()

Removed in Java 11

// Removed in Java 11 (in addition to the Java EE & Corba modules)
java.lang.Runtime.runFinalizersOnExit(...)
java.lang.SecurityManager.checkAwtEventQueueAccess()
java.lang.SecurityManager.checkMemberAccess(...)
java.lang.SecurityManager.checkSystemClipboardAccess()
java.lang.SecurityManager.checkTopLevelWindow(...)
java.lang.System.runFinalizersOnExit(...)
javax.security.auth.Policy

To be Removed in a Future Release

java.io.FileInputStream.finalize()
java.io.FileOutputStream.finalize()
java.lang.Compiler
java.lang.Runtime.traceInstructions(...)
java.lang.Runtime.traceInstructions(...)
java.lang.Thread.countStackFrames()
java.security.acl
java.security.Certificate
java.security.Identity
java.security.IdentityScope
java.security.Signer
java.util.jar.Pack200
java.util.zip.Deflater.finalize()
java.util.zip.Inflater.finalize()
java.util.zip.ZipFile.finalize()
javax.management.remote.rmi.RMIConnectorServer.CREDENTIAL_TYPES

Solution of Removed Packages/Classes

compile 'com.sun.activation:javax.activation:1.2.0'
compile 'javax.transaction:javax.transaction-api:1.3'
compile 'com.sun.xml.bind:jaxb-impl:2.3.1'
compile 'com.sun.xml.ws:jaxws-ri:2.3.1'
compile 'javax.annotation:javax.annotation-api:1.3.2'

Check Deprecated packages

$ jdeps --jdk-internals app.jar

Oracle JDK LTS

Java 11 and next 17

Java 9 (91 JEPS)

102: Process API Updates
110: HTTP 2 Client
143: Improve Contended Locking
158: Unified JVM Logging
165: Compiler Control
193: Variable Handles
197: Segmented Code Cache
199: Smart Java Compilation, Phase Two
200: The Modular JDK
201: Modular Source Code
211: Elide Deprecation Warnings on Import Statements
212: Resolve Lint and Doclint Warnings
213: Milling Project Coin
214: Remove GC Combinations Deprecated in JDK 8
215: Tiered Attribution for javac
216: Process Import Statements Correctly
217: Annotations Pipeline 2.0
219: Datagram Transport Layer Security (DTLS)
220: Modular Run-Time Images
221: Simplified Doclet API
222: jshell: The Java Shell (Read-Eval-Print Loop)
223: New Version-String Scheme
224: HTML5 Javadoc
225: Javadoc Search
226: UTF-8 Property Files
227: Unicode 7.0
228: Add More Diagnostic Commands
229: Create PKCS12 Keystores by Default
231: Remove Launch-Time JRE Version Selection
232: Improve Secure Application Performance
233: Generate Run-Time Compiler Tests Automatically
235: Test Class-File Attributes Generated by javac
236: Parser API for Nashorn
237: Linux/AArch64 Port
238: Multi-Release JAR Files
240: Remove the JVM TI hprof Agent
241: Remove the jhat Tool
243: Java-Level JVM Compiler Interface
244: TLS Application-Layer Protocol Negotiation Extension
245: Validate JVM Command-Line Flag Arguments
246: Leverage CPU Instructions for GHASH and RSA
247: Compile for Older Platform Versions
248: Make G1 the Default Garbage Collector
249: OCSP Stapling for TLS
250: Store Interned Strings in CDS Archives
251: Multi-Resolution Images
252: Use CLDR Locale Data by Default
253: Prepare JavaFX UI Controls & CSS APIs for Modularization
254: Compact Strings
255: Merge Selected Xerces 2.11.0 Updates into JAXP
256: BeanInfo Annotations
257: Update JavaFX/Media to Newer Version of GStreamer
258: HarfBuzz Font-Layout Engine
259: Stack-Walking API
260: Encapsulate Most Internal APIs
261: Module System
262: TIFF Image I/O
263: HiDPI Graphics on Windows and Linux
264: Platform Logging API and Service
265: Marlin Graphics Renderer
266: More Concurrency Updates
267: Unicode 8.0
268: XML Catalogs
269: Convenience Factory Methods for Collections
270: Reserved Stack Areas for Critical Sections
271: Unified GC Logging
272: Platform-Specific Desktop Features
273: DRBG-Based SecureRandom Implementations
274: Enhanced Method Handles
275: Modular Java Application Packaging
276: Dynamic Linking of Language-Defined Object Models
277: Enhanced Deprecation
278: Additional Tests for Humongous Objects in G1
279: Improve Test-Failure Troubleshooting
280: Indify String Concatenation
281: HotSpot C++ Unit-Test Framework
282: jlink: The Java Linker
283: Enable GTK 3 on Linux
284: New HotSpot Build System
285: Spin-Wait Hints
287: SHA-3 Hash Algorithms
288: Disable SHA-1 Certificates
289: Deprecate the Applet API
290: Filter Incoming Serialization Data
291: Deprecate the Concurrent Mark Sweep (CMS) Garbage Collector
292: Implement Selected ECMAScript 6 Features in Nashorn
294: Linux/s390x Port
295: Ahead-of-Time Compilation
297: Unified arm32/arm64 Port
298: Remove Demos and Samples
299: Reorganize Documentation

https://openjdk.java.net/projects/jdk9/

Java 10 (12 JEPS)

JEP 286: Local-Variable Type Inference  https://openjdk.java.net/jeps/286
JEP 296: Consolidate the JDK Forest into a Single Repository    https://openjdk.java.net/jeps/296
JEP 304: Garbage Collector Interface    https://openjdk.java.net/jeps/304
JEP 307: Parallel Full GC for G1    https://openjdk.java.net/jeps/307
JEP 310: Application Class-Data Sharing https://openjdk.java.net/jeps/310
JEP 312: Thread-Local Handshakes    https://openjdk.java.net/jeps/312
JEP 313: Remove the Native-Header Generation Tool (javah)   https://openjdk.java.net/jeps/313
JEP 314: Additional Unicode Language-Tag Extensions https://openjdk.java.net/jeps/314
JEP 316: Heap Allocation on Alternative Memory Devices  https://openjdk.java.net/jeps/316
JEP 317: Experimental Java-Based JIT Compiler   https://openjdk.java.net/jeps/317
JEP 319: Root Certificates  https://openjdk.java.net/jeps/319
JEP 322: Time-Based Release Versioning  https://openjdk.java.net/jeps/322

https://openjdk.java.net/projects/jdk/10/

Java 11 (17 JEPS)

JEP 181: Nest-Based Access Control  https://openjdk.java.net/jeps/181
JEP 309: Dynamic Class-File Constants   https://openjdk.java.net/jeps/309
JEP 315: Improve Aarch64 Intrinsics https://openjdk.java.net/jeps/315
JEP 318: Epsilon: A No-Op Garbage Collector (Experimental)  https://openjdk.java.net/jeps/318
JEP 320: Remove the Java EE and CORBA Modules   https://openjdk.java.net/jeps/320
JEP 321: HTTP Client (Standard) https://openjdk.java.net/jeps/321
JEP 323: Local-Variable Syntax for Lambda Parameters    https://openjdk.java.net/jeps/323
JEP 324: Key Agreement with Curve25519 and Curve448 https://openjdk.java.net/jeps/324
JEP 327: Unicode 10 https://openjdk.java.net/jeps/327
JEP 328: Flight Recorder    https://openjdk.java.net/jeps/328
JEP 329: ChaCha20 and Poly1305 Cryptographic Algorithms https://openjdk.java.net/jeps/329
JEP 330: Launch Single-File Source-Code Programs    https://openjdk.java.net/jeps/330
JEP 331: Low-Overhead Heap Profiling    https://openjdk.java.net/jeps/331
JEP 332: Transport Layer Security (TLS) 1.3 https://openjdk.java.net/jeps/332
JEP 333: ZGC: A Scalable Low-Latency Garbage Collector (Experimental)   https://openjdk.java.net/jeps/333
JEP 335: Deprecate the Nashorn JavaScript Engine    https://openjdk.java.net/jeps/335
JEP 336: Deprecate the Pack200 Tools and API    https://openjdk.java.net/jeps/336

https://openjdk.java.net/projects/jdk/11/

Java 12- OpenJDK Projects

Amber: Right-sizing language cermenony   https://openjdk.java.net/projects/amber/
Loom: Continuations & Fibres    https://openjdk.java.net/projects/loom/
Panama: Foreign-function/data interface https://openjdk.java.net/projects/panama/  
Valhalla: Value types & specialized generics    https://openjdk.java.net/projects/valhalla/

https://openjdk.java.net/

November 1, 2018

JSF UI Components Overview

https://www.javatpoint.com/jsf-ui-components

https://www.tutorialspoint.com/jsf/index.htm

Security Testing in the Build Pipeline

PMD

PMD is a source code analyzer tool, but also have Copy/Paste Detector tool (CPD). https://maven.apache.org/plugins/maven-pmd-plugin/index.html

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    ...
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-pmd-plugin</artifactId>
                <version>3.11.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>check</goal>
                            <goal>cpd-check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            ...
        </plugins>
    </build>
</project>

Reference

  1. https://maven.apache.org/plugins/maven-pmd-plugin/plugin-info.html
  2. https://maven.apache.org/plugins/maven-pmd-plugin/examples/violationChecking.html
  3. https://docs.sonarqube.org/display/PLUG/Importing+SpotBugs%2C+FindSecBugs%2C+PMD%2C+Checkstyle+Issues+Reports

OWASP Dependency Check

Scan dependencies for known CVE against National Vulnerability Database, NVD.

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    ...
    <build>
        <plugins>
            <plugin>
                <groupId>org.owasp</groupId>
                <artifactId>dependency-check-maven</artifactId>
                <version>3.3.4</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            ...
        </plugins>
    </build>
</project>

Reference

  1. https://magnus-k-karlsson.blogspot.com/2018/10/owasp-dependency-check.html

SpotBugs

"SpotBugs is a program to find bugs in Java programs. It looks for instances of “bug patterns” — code instances that are likely to be errors." https://spotbugs.readthedocs.io/en/latest/introduction.html

<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    ...
    <build>
        <plugins>
            <plugin>
                <groupId>com.github.spotbugs</groupId>
                <artifactId>spotbugs-maven-plugin</artifactId>
                <version>3.1.7</version>
                <configuration>
                    <effort>Max</effort>
                    <threshold>Low</threshold>
                    <failOnError>true</failOnError>
                    <includeFilterFile>${project.basedir}/spotbugs-security-include.xml</includeFilterFile>
                    <excludeFilterFile>${project.basedir}/spotbugs-security-exclude.xml</excludeFilterFile>
                    <plugins>
                        <plugin>
                            <groupId>com.h3xstream.findsecbugs</groupId>
                            <artifactId>findsecbugs-plugin</artifactId>
                            <version>LATEST</version> <!-- Auto-update to the latest stable -->
                        </plugin>
                    </plugins>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

Reference

  1. https://github.com/find-sec-bugs/find-sec-bugs/wiki/Maven-configuration
  2. https://spotbugs.readthedocs.io/en/latest/maven.html
  3. https://find-sec-bugs.github.io/tutorials.htm

BDD

"The framework is essentially a set of Cucumber-JVM features that are pre-wired with Selenium/WebDriver, OWASP ZAP, SSLyze and Tennable's Nessus scanner." https://github.com/continuumsecurity/bdd-security

# Test App
$ wget https://github.com/continuumsecurity/RopeyTasks/raw/master/ropeytasks.jar
$ java -jar ropeytasks.jar

# Download framework and run extensive test
$ git clone https://github.com/continuumsecurity/bdd-security.git
$ ./gradlew
  1. https://github.com/continuumsecurity/bdd-security
  2. https://github.com/continuumsecurity/bdd-security/wiki/2-Getting-Started