filters = new HashMap<>();
39 | filters.put("anon", new AnonymousFilter());
40 | filters.put("authc", new FormAuthenticationFilter());
41 | LogoutFilter logoutFilter = new LogoutFilter();
42 | logoutFilter.setRedirectUrl("/login?logout");
43 | filters.put("logout", logoutFilter);
44 | filters.put("roles", new RolesAuthorizationFilter());
45 | filters.put("user", new UserFilter());
46 | shiroFilter.setFilters(filters);
47 | return (AbstractShiroFilter) shiroFilter.getObject();
48 | }
49 |
50 | @Bean(name = "securityManager")
51 | public DefaultWebSecurityManager securityManager() {
52 | DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
53 | securityManager.setRealm(jdbcRealm());
54 | return securityManager;
55 | }
56 |
57 | @Autowired
58 | private DataSource dataSource;
59 |
60 | @Bean(name = "jdbcRealm")
61 | @DependsOn("lifecycleBeanPostProcessor")
62 | public JdbcRealm jdbcRealm() {
63 | JdbcRealm realm = new JdbcRealm();
64 | HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
65 | credentialsMatcher.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
66 | realm.setCredentialsMatcher(credentialsMatcher);
67 | realm.setDataSource(dataSource);
68 | realm.init();
69 | return realm;
70 | }
71 |
72 | @Bean
73 | public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
74 | return new LifecycleBeanPostProcessor();
75 | }
76 | }
--------------------------------------------------------------------------------
/apache-shiro/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | #debug=true
2 |
3 | server.port=8443
4 | server.ssl.key-store=classpath:ssl.keystore
5 | server.ssl.key-store-password=secret
6 |
7 | logging.level.org.apache.shiro=debug
--------------------------------------------------------------------------------
/apache-shiro/src/main/resources/data.sql:
--------------------------------------------------------------------------------
1 | -- admin/adminjdbc
2 | insert into users values ('admin', '22f256eca1f336a97eef2b260773cb0d81d900c208ff26e94410d292d605fed8', true);
3 | insert into user_roles values ('admin', 'guest');
--------------------------------------------------------------------------------
/apache-shiro/src/main/resources/schema.sql:
--------------------------------------------------------------------------------
1 | create table if not exists users (
2 | username varchar(256),
3 | password varchar(256),
4 | enabled boolean
5 | );
6 |
7 | create table if not exists user_roles (
8 | username varchar(256),
9 | role_name varchar(256)
10 | );
--------------------------------------------------------------------------------
/apache-shiro/src/main/resources/shiro-users.properties:
--------------------------------------------------------------------------------
1 | user.user = 04f8996da763b7a969b1028ee3007569eaf3a635486ddab211d512c85b9df8fb,guest
2 | user.admin = 8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918,guest
--------------------------------------------------------------------------------
/apache-shiro/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Apache Shiro
6 |
7 |
8 | Hello Apache Shiro!
9 |
10 |
--------------------------------------------------------------------------------
/apache-shiro/src/main/resources/templates/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Login
5 |
6 |
7 |
8 | Sign In
9 |
10 |
11 | Please enter your username and password to login.
12 |
13 |
14 |
15 | Invalid username and password.
16 |
17 |
18 | You have been logged out.
19 |
20 |
21 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/apache-shiro/src/test/java/security/ApplicationTests.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2002-2013 the original author or authors.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package security;
17 |
18 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
19 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
20 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
21 |
22 | import org.apache.shiro.web.servlet.AbstractShiroFilter;
23 | import org.junit.Before;
24 | import org.junit.Test;
25 | import org.junit.runner.RunWith;
26 | import org.springframework.beans.factory.annotation.Autowired;
27 | import org.springframework.boot.test.SpringApplicationConfiguration;
28 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
29 | import org.springframework.test.context.web.WebAppConfiguration;
30 | import org.springframework.test.web.servlet.MockMvc;
31 | import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
32 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
33 | import org.springframework.web.context.WebApplicationContext;
34 |
35 | @RunWith(SpringJUnit4ClassRunner.class)
36 | @SpringApplicationConfiguration(classes = Application.class)
37 | @WebAppConfiguration
38 | public class ApplicationTests {
39 | @Autowired
40 | WebApplicationContext context;
41 | @Autowired
42 | AbstractShiroFilter shiroFilter;
43 |
44 | MockMvc mockMvc;
45 |
46 | @Before
47 | public void setup() {
48 | mockMvc = MockMvcBuilders
49 | .webAppContextSetup(context)
50 | .addFilters(shiroFilter)
51 | .alwaysDo(print())
52 | .build();
53 | }
54 |
55 | @Test
56 | public void apiNeedsAuthentication() throws Exception {
57 | mockMvc
58 | .perform(get("/api/health"))
59 | .andExpect(status().is3xxRedirection());
60 | }
61 |
62 | @Test
63 | public void loginSuccess() throws Exception {
64 | MockHttpServletRequestBuilder loginRequest = post("/login")
65 | .param("username", "admin")
66 | .param("password", "adminjdbc");
67 | mockMvc
68 | .perform(loginRequest)
69 | .andExpect(status().is3xxRedirection());
70 | }
71 |
72 | @Test
73 | public void invalidUsernamePassword() throws Exception {
74 | MockHttpServletRequestBuilder loginRequest = post("/login")
75 | .param("username", "admin")
76 | .param("password", "INVALID");
77 | mockMvc
78 | .perform(loginRequest)
79 | .andExpect(status().isOk());
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/javaee7-security/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.raibledesigns
7 | javaee7-security
8 | 0.0.1-SNAPSHOT
9 | war
10 |
11 | Java EE Web Application Security Example
12 |
13 | An example project showing how to configure security with Java EE.
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-parent
19 | 1.2.5.RELEASE
20 |
21 |
22 |
23 |
24 |
25 | UTF-8
26 | 1.8
27 |
28 |
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-starter-web
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-jersey
37 |
38 |
39 | org.springframework.boot
40 | spring-boot-starter-tomcat
41 | provided
42 |
43 |
44 | org.glassfish.jersey.containers.glassfish
45 | jersey-gf-cdi
46 | ${jersey.version}
47 |
48 |
49 | org.glassfish.jersey.containers.glassfish
50 | jersey-gf-cdi-ban-custom-hk2-binding
51 | ${jersey.version}
52 |
53 |
54 | org.jboss.weld.servlet
55 | weld-servlet-core
56 | 2.2.5.Final
57 | runtime
58 |
59 |
60 |
61 |
62 |
63 |
64 | org.eclipse.jetty
65 | jetty-maven-plugin
66 | 9.2.10.v20150310
67 |
68 |
69 |
70 | Java EE Login
71 | ${basedir}/src/test/resources/jdbc-realm.properties
72 |
73 |
74 |
75 | ${basedir}/src/test/resources/jetty.xml,${basedir}/src/test/resources/jetty-http.xml,${basedir}/src/test/resources/jetty-ssl.xml,${basedir}/src/test/resources/jetty-https.xml
76 |
77 | ${basedir}/src/main/webapp/WEB-INF/jetty-context.xml
78 |
79 | ${basedir}/src/main/webapp/WEB-INF/jetty-env.xml
80 |
81 |
82 |
83 |
84 |
85 | mysql
86 | mysql-connector-java
87 | 5.1.36
88 |
89 |
90 |
91 |
92 | org.codehaus.mojo
93 | keytool-maven-plugin
94 | 1.5
95 |
96 |
97 | generate-resources
98 | clean
99 |
100 | clean
101 |
102 |
103 |
104 | generate-resources
105 | generateKeyPair
106 |
107 | generateKeyPair
108 |
109 |
110 |
111 |
112 | ${project.build.directory}/ssl.keystore
113 | cn=localhost
114 | secret
115 | secret
116 | localhost
117 | RSA
118 |
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/java/security/Health.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import javax.xml.bind.annotation.XmlRootElement;
4 |
5 | @XmlRootElement(namespace = "security.health")
6 | public class Health {
7 |
8 | private String status;
9 |
10 | protected Health() {
11 | }
12 |
13 | public Health(String status) {
14 | this.status = status;
15 | }
16 |
17 | public String getStatus() {
18 | return status;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/java/security/HealthResource.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import javax.ws.rs.GET;
4 | import javax.ws.rs.Path;
5 | import javax.ws.rs.Produces;
6 |
7 | @Path("/health")
8 | public class HealthResource {
9 | @GET
10 | @Produces("application/json")
11 | public Health health() {
12 | return new Health("Jersey is up and running!");
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/java/security/JavaEELoginService.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import org.apache.commons.logging.Log;
4 | import org.apache.commons.logging.LogFactory;
5 |
6 | import javax.inject.Inject;
7 | import javax.servlet.ServletException;
8 | import javax.servlet.http.HttpServletRequest;
9 |
10 | public class JavaEELoginService implements LoginService {
11 | private Log log = LogFactory.getLog(JavaEELoginService.class);
12 |
13 | @Inject
14 | private HttpServletRequest request;
15 |
16 | public LoginStatus getStatus() {
17 | if (request.getRemoteUser() != null) {
18 | return new LoginStatus(true, request.getRemoteUser());
19 | } else {
20 | return new LoginStatus(false, null);
21 | }
22 | }
23 |
24 | @Override
25 | public LoginStatus login(String username, String password) {
26 | try {
27 | if (request.getRemoteUser() == null) {
28 | request.login(username, password);
29 | log.debug("Login succeeded!");
30 | }
31 | return new LoginStatus(true, request.getRemoteUser());
32 | } catch (ServletException e) {
33 | e.printStackTrace();
34 | return new LoginStatus(false, null);
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/java/security/LoginResource.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import javax.inject.Inject;
4 | import javax.ws.rs.*;
5 | import javax.ws.rs.core.MediaType;
6 |
7 | @Path("/login")
8 | @Produces(MediaType.APPLICATION_JSON)
9 | @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
10 | public class LoginResource {
11 |
12 | @Inject
13 | LoginService loginService;
14 |
15 | @GET
16 | public LoginStatus getStatus() {
17 | return loginService.getStatus();
18 | }
19 |
20 | @POST
21 | public LoginStatus login(@FormParam("j_username") String username,
22 | @FormParam("j_password") String password) {
23 | return loginService.login(username, password);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/java/security/LoginService.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | public interface LoginService {
4 |
5 | LoginStatus getStatus();
6 |
7 | LoginStatus login(String username, String password);
8 | }
9 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/java/security/LoginStatus.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | public class LoginStatus {
4 |
5 | private final boolean loggedIn;
6 | private final String username;
7 |
8 | public LoginStatus(boolean loggedIn, String username) {
9 | this.loggedIn = loggedIn;
10 | this.username = username;
11 | }
12 |
13 | public boolean isLoggedIn() {
14 | return loggedIn;
15 | }
16 |
17 | public String getUsername() {
18 | return username;
19 | }
20 | }
--------------------------------------------------------------------------------
/javaee7-security/src/main/java/security/LogoutResource.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import javax.inject.Inject;
4 | import javax.servlet.ServletException;
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.ws.rs.*;
7 | import javax.ws.rs.core.MediaType;
8 |
9 | @Path("/logout")
10 | @Produces(MediaType.TEXT_PLAIN)
11 | public class LogoutResource {
12 |
13 | @Inject
14 | HttpServletRequest request;
15 |
16 | @GET
17 | public String getStatus() throws ServletException {
18 | request.logout();
19 | request.getSession().invalidate();
20 | return "OK";
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/webapp/WEB-INF/beans.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/webapp/WEB-INF/jetty-context.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | - -org.eclipse.jetty.servlet.ServletContextHandler.Decorator
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/webapp/WEB-INF/jetty-env.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | BeanManager
10 |
11 |
12 | javax.enterprise.inject.spi.BeanManager
13 | org.jboss.weld.resources.ManagerObjectFactory
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/webapp/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | jerseyServlet
9 | org.glassfish.jersey.servlet.ServletContainer
10 |
11 | jersey.config.server.provider.packages
12 |
13 | security
14 |
15 |
16 |
17 |
18 |
19 | jerseyServlet
20 | /api/*
21 |
22 |
23 |
24 |
25 | api
26 | /api/health
27 | GET
28 | POST
29 |
30 |
31 | ROLE_USER
32 | ROLE_ADMIN
33 |
34 |
35 | CONFIDENTIAL
36 |
37 |
38 |
39 |
40 | FORM
41 | Java EE Login
42 |
43 | /login.jsp
44 | /login.jsp?error=true
45 |
46 |
47 |
48 |
49 | ROLE_USER
50 |
51 |
52 |
53 | ROLE_ADMIN
54 |
55 |
56 |
57 | BeanManager
58 |
59 | javax.enterprise.inject.spi.BeanManager
60 |
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/webapp/index.jsp:
--------------------------------------------------------------------------------
1 | Hello Java EE!
2 |
--------------------------------------------------------------------------------
/javaee7-security/src/main/webapp/login.jsp:
--------------------------------------------------------------------------------
1 | <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
2 |
3 |
4 |
5 |
6 | Login
7 |
8 |
9 |
10 | Please enter your username and password to login.
11 |
12 |
13 |
25 |
26 |
27 |
28 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/javaee7-security/src/test/resources/jdbc-realm.properties:
--------------------------------------------------------------------------------
1 | # If H2 is used, error is:
2 |
3 | # 2011-04-18 12:21:32.291:WARN::UserRealm Java EE Login could not connect to database; will try later
4 | # org.h2.jdbc.JdbcSQLException: Database may be already in use: "Locked by another process".
5 | # Possible solutions: close all other connection(s); use the server mode [90020-154]
6 |
7 | jdbcdriver = com.mysql.jdbc.Driver
8 | url = jdbc:mysql://localhost/appfuse
9 | username = root
10 | password =
11 | usertable = app_user
12 | usertablekey = id
13 | usertableuserfield = username
14 | usertablepasswordfield = password
15 | roletable = role
16 | roletablekey = id
17 | roletablerolefield = name
18 | userroletable = user_role
19 | userroletableuserkey = user_id
20 | userroletablerolekey = role_id
21 | cachetime = 300
--------------------------------------------------------------------------------
/javaee7-security/src/test/resources/jetty-http.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
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 |
42 |
--------------------------------------------------------------------------------
/javaee7-security/src/test/resources/jetty-https.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | -
29 |
30 | http/1.1
31 |
32 |
33 |
34 | -
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/javaee7-security/src/test/resources/jetty-ssl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | /
11 |
12 |
13 | /
14 |
15 |
16 |
17 |
18 | - SSL_RSA_WITH_DES_CBC_SHA
19 | - SSL_DHE_RSA_WITH_DES_CBC_SHA
20 | - SSL_DHE_DSS_WITH_DES_CBC_SHA
21 | - SSL_RSA_EXPORT_WITH_RC4_40_MD5
22 | - SSL_RSA_EXPORT_WITH_DES40_CBC_SHA
23 | - SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA
24 | - SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/javaee7-security/src/test/resources/jetty.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | https
9 |
10 | 32768
11 | 8192
12 | 8192
13 | true
14 | false
15 | 512
16 |
17 |
22 |
23 |
--------------------------------------------------------------------------------
/javaee7-security/src/test/resources/realm.properties:
--------------------------------------------------------------------------------
1 | #
2 | # This file defines users passwords and roles for a HashUserRealm
3 | #
4 | # The format is
5 | # : [, ...]
6 | #
7 | # Passwords may be clear text, obfuscated or checksummed. The class
8 | # org.eclipse.util.Password should be used to generate obfuscated
9 | # passwords or password checksums
10 | #
11 | # If DIGEST Authentication is used, the password must be in a recoverable
12 | # format, either plain text or OBF:.
13 | #
14 | jetty: MD5:164c88b302622e17050af52c89945d44,ROLE_USER
15 | admin: CRYPT:adpexzg3FUZAk,ROLE_ADMIN
16 | other: OBF:1xmk1w261u9r1w1c1xmq,ROLE_USER
17 | plain: plain,ROLE_USER
18 | user: user,ROLE_USER
19 |
20 | # This entry is for digest auth. The credential is a MD5 hash of username:realmname:password
21 | digest: MD5:6e120743ad67abfbc385bc2bb754e297,ROLE_USER
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
19 |
20 | 4.0.0
21 |
22 | com.raibledesigns.security
23 | java-webapp-security-examples
24 | pom
25 | Java Web App Security Examples
26 | 1.0-SNAPSHOT
27 |
28 |
29 | Security implementation demonstrations with Java EE 7, Spring Security and Apache Shiro.
30 |
31 |
32 |
33 | scm:git:git@github.com/mraible/java-webapp-security-examples.git
34 | scm:git:git@github.com/mraible/java-webapp-security-examples.git
35 | https://github.com/mraible/java-webapp-security-examples
36 |
37 |
38 |
39 | javaee7-security
40 | spring-security
41 | apache-shiro
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/spring-security/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 | 4.0.0
5 |
6 | com.raibledesigns.security
7 | spring-security
8 | 0.0.1-SNAPSHOT
9 | war
10 |
11 | Spring Security Web Application Example
12 |
13 | An example project showing how to configure BASIC, Form and API authentication with Spring Security.
14 |
15 |
16 |
17 | org.springframework.boot
18 | spring-boot-starter-parent
19 | 1.2.5.RELEASE
20 |
21 |
22 |
23 |
24 |
25 | UTF-8
26 | 1.8
27 | 4.0.3.RELEASE
28 |
29 |
30 |
31 |
32 | org.springframework.boot
33 | spring-boot-starter-security
34 |
35 |
36 | org.springframework.boot
37 | spring-boot-starter-web
38 |
39 |
40 | org.springframework.boot
41 | spring-boot-starter-thymeleaf
42 |
43 |
44 | org.springframework.boot
45 | spring-boot-starter-jdbc
46 |
47 |
48 | com.h2database
49 | h2
50 |
51 |
52 | net.sourceforge.nekohtml
53 | nekohtml
54 | 1.9.21
55 |
56 |
57 | org.springframework.boot
58 | spring-boot-starter-test
59 | test
60 |
61 |
62 | org.springframework.security
63 | spring-security-test
64 | test
65 |
66 |
67 |
68 |
69 |
70 |
71 | org.springframework.boot
72 | spring-boot-maven-plugin
73 |
74 |
75 | org.codehaus.mojo
76 | keytool-maven-plugin
77 | 1.5
78 |
79 |
80 | generate-resources
81 | clean
82 |
83 | clean
84 |
85 |
86 |
87 | generate-resources
88 | generateKeyPair
89 |
90 | generateKeyPair
91 |
92 |
93 |
94 |
95 | ${project.build.directory}/classes/ssl.keystore
96 | cn=localhost
97 | secret
98 | secret
99 | localhost
100 | RSA
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/spring-security/src/main/java/security/Application.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class Application {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(Application.class, args);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/spring-security/src/main/java/security/CsrfController.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import org.springframework.security.web.csrf.CsrfToken;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.bind.annotation.RestController;
6 |
7 | @RestController
8 | public class CsrfController {
9 |
10 | @RequestMapping(value="/api/csrf",produces="application/json")
11 | public CsrfToken csrf(CsrfToken token) {
12 | return token;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/spring-security/src/main/java/security/Health.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | public class Health {
4 |
5 | private String status;
6 |
7 | protected Health() {}
8 |
9 | public Health(String status) {
10 | this.status = status;
11 | }
12 |
13 | public String getStatus() {
14 | return status;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/spring-security/src/main/java/security/HealthResource.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import org.springframework.web.bind.annotation.RequestMapping;
4 | import org.springframework.web.bind.annotation.RestController;
5 |
6 | @RestController
7 | public class HealthResource {
8 |
9 | @RequestMapping("/api/health")
10 | public Health health() {
11 | return new Health("Spring Boot is up and running!");
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/spring-security/src/main/java/security/LoginController.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import org.springframework.security.core.Authentication;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 | import org.springframework.web.bind.annotation.RestController;
6 |
7 | @RestController
8 | @RequestMapping("/api/login")
9 | public class LoginController {
10 |
11 | @RequestMapping
12 | public LoginStatus status(Authentication principal) {
13 |
14 | return principal == null ? new LoginStatus(false, null) : new LoginStatus(true, principal.getName());
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/spring-security/src/main/java/security/LoginStatus.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | public class LoginStatus {
4 |
5 | private final boolean loggedIn;
6 | private final String username;
7 |
8 | public LoginStatus(boolean loggedIn, String username) {
9 | this.loggedIn = loggedIn;
10 | this.username = username;
11 | }
12 |
13 | public boolean isLoggedIn() {
14 | return loggedIn;
15 | }
16 |
17 | public String getUsername() {
18 | return username;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/spring-security/src/main/java/security/MvcConfig.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
5 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
6 |
7 | @Configuration
8 | public class MvcConfig extends WebMvcConfigurerAdapter {
9 |
10 | @Override
11 | public void addViewControllers(ViewControllerRegistry registry) {
12 | registry.addViewController("/").setViewName("index");
13 | registry.addViewController("/login").setViewName("login");
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/spring-security/src/main/java/security/ServletInitializer.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import org.springframework.boot.builder.SpringApplicationBuilder;
4 | import org.springframework.boot.context.web.SpringBootServletInitializer;
5 |
6 | public class ServletInitializer extends SpringBootServletInitializer {
7 |
8 | @Override
9 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
10 | return application.sources(Application.class);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/spring-security/src/main/java/security/WebSecurityConfig.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
6 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
7 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
9 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
10 |
11 | import javax.sql.DataSource;
12 |
13 | @Configuration
14 | @EnableWebSecurity
15 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
16 |
17 | @Autowired
18 | private DataSource dataSource;
19 |
20 | @Override
21 | protected void configure(HttpSecurity http) throws Exception {
22 | http
23 | .authorizeRequests()
24 | .antMatchers("/","/api/login","/api/csrf").permitAll()
25 | .anyRequest().authenticated()
26 | .and()
27 | .formLogin()
28 | .loginPage("/login")
29 | .permitAll()
30 | .and()
31 | .httpBasic()
32 | .and()
33 | .logout()
34 | .permitAll()
35 | .and()
36 | .rememberMe();
37 | }
38 |
39 | @Autowired
40 | public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
41 | auth
42 | .jdbcAuthentication()
43 | .dataSource(this.dataSource)
44 | .passwordEncoder(new BCryptPasswordEncoder());
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/spring-security/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | security.basic.enabled=false
2 | server.port=8443
3 | server.ssl.key-store=classpath:ssl.keystore
4 | server.ssl.key-store-password=secret
5 |
6 | spring.thymeleaf.cache=false
7 | spring.thymeleaf.mode=LEGACYHTML5
8 |
9 | debug: true
10 | logging.level.org.springframework.security: INFO
11 |
--------------------------------------------------------------------------------
/spring-security/src/main/resources/data.sql:
--------------------------------------------------------------------------------
1 | -- user/password
2 | insert into users (username, password, enabled) values ('user', '$2a$10$OAhL3e1unzuYdSHfRTtwxu9ofhJxa8JQ01XzZhZ3zfXWygvDF6lOS', true);
3 |
4 | insert into authorities (username, authority) values ('user', 'ROLE_ADMIN');
5 |
--------------------------------------------------------------------------------
/spring-security/src/main/resources/schema.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS users (
2 | username VARCHAR(256),
3 | password VARCHAR(256),
4 | enabled BOOLEAN
5 | );
6 |
7 | CREATE TABLE IF NOT EXISTS authorities (
8 | username VARCHAR(256),
9 | authority VARCHAR(256)
10 | );
11 |
--------------------------------------------------------------------------------
/spring-security/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Spring Security
6 |
7 |
8 | Hello Spring Security!
9 |
10 |
--------------------------------------------------------------------------------
/spring-security/src/main/resources/templates/login.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Spring Security Example
5 |
6 |
7 |
8 | Sign In
9 |
10 |
11 | Please enter your username and password to login.
12 |
13 |
14 |
15 | Invalid username and password.
16 |
17 |
18 | You have been logged out.
19 |
20 |
21 |
32 |
33 |
34 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/spring-security/src/test/java/security/ApplicationTests.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.test.context.web.WebAppConfiguration;
6 | import org.springframework.boot.test.SpringApplicationConfiguration;
7 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
8 |
9 | @RunWith(SpringJUnit4ClassRunner.class)
10 | @SpringApplicationConfiguration(classes = Application.class)
11 | @WebAppConfiguration
12 | public class ApplicationTests {
13 |
14 | @Test
15 | public void contextLoads() {
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/spring-security/src/test/java/security/MockMvcWebSecurityTests.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import static org.hamcrest.CoreMatchers.*;
4 | import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*;
5 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*;
6 | import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.*;
7 | import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*;
8 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
9 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
10 | import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
11 |
12 | import org.junit.Before;
13 | import org.junit.Test;
14 | import org.junit.runner.RunWith;
15 | import org.springframework.beans.factory.annotation.Autowired;
16 | import org.springframework.boot.test.SpringApplicationConfiguration;
17 | import org.springframework.http.MediaType;
18 | import org.springframework.security.test.context.support.WithMockUser;
19 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
20 | import org.springframework.test.context.web.WebAppConfiguration;
21 | import org.springframework.test.web.servlet.MockMvc;
22 | import org.springframework.test.web.servlet.setup.MockMvcBuilders;
23 | import org.springframework.web.context.WebApplicationContext;
24 |
25 | /**
26 | * Demonstrates some of the integrations with Spring Security's Test support.
27 | * This is not a complete showcase. For additional features and details on what
28 | * is shown refer to the reference
31 | *
32 | * @author Rob Winch
33 | */
34 | @RunWith(SpringJUnit4ClassRunner.class)
35 | @SpringApplicationConfiguration(classes = {Application.class})
36 | @WebAppConfiguration
37 | public class MockMvcWebSecurityTests {
38 | @Autowired
39 | WebApplicationContext context;
40 |
41 | MockMvc mockMvc;
42 |
43 | @Before
44 | public void setup() {
45 | mockMvc = MockMvcBuilders
46 | .webAppContextSetup(context)
47 | .apply(springSecurity())
48 | .alwaysDo(print())
49 | .build();
50 | }
51 |
52 | /**
53 | * Easily make and verify a request to the home page
54 | */
55 | @Test
56 | public void testHome() throws Exception {
57 | mockMvc
58 | .perform(get("/"))
59 | .andExpect(status().isOk())
60 | .andExpect(content().string(containsString("Spring")));
61 | }
62 |
63 | /**
64 | * Demonstrate how to easily make a form based login request.
65 | *
66 | * - Default username is "user"
67 | * - Default password is "password"
68 | * - Automatically includes a valid CSRF token
69 | * - We are able to verify the user we are authenticated with
70 | *
71 | */
72 | @Test
73 | public void testLogin() throws Exception {
74 | mockMvc
75 | .perform(formLogin())
76 | .andExpect(status().isFound())
77 | .andExpect(redirectedUrl("/"))
78 | .andExpect(authenticated().withUsername("user"));
79 | }
80 |
81 | /**
82 | * Demonstrate how to easily make a form based login request.
83 | *
84 | *
85 | * - Default username is "user"
86 | * - Override the default password to "invalid"
87 | * - Automatically includes a valid CSRF token
88 | * - We are able to verify we are unauthenticated
89 | *
90 | */
91 | @Test
92 | public void testDenied() throws Exception {
93 | String loginErrorUrl = "/login?error";
94 | mockMvc
95 | .perform(formLogin().password("invalid"))
96 | .andExpect(status().isFound())
97 | .andExpect(redirectedUrl( loginErrorUrl))
98 | .andExpect(unauthenticated());
99 |
100 | mockMvc
101 | .perform(get(loginErrorUrl))
102 | .andExpect(content().string(containsString("Invalid username and password")));
103 | }
104 |
105 | /**
106 | * Demonstrates requesting a protected page as an unauthenticated user
107 | */
108 | @Test
109 | public void testProtected() throws Exception {
110 | mockMvc
111 | .perform(get("/api/health").accept(MediaType.APPLICATION_JSON))
112 | .andExpect(status().isUnauthorized());
113 | }
114 |
115 | /**
116 | * Demonstrates requesting a protected page with valid http basic credentials
117 | */
118 | @Test
119 | public void testAuthorizedAccessHttpBasic() throws Exception {
120 | mockMvc
121 | .perform(get("/api/health").with(httpBasic("user", "password")))
122 | .andExpect(status().isOk());
123 | }
124 |
125 | /**
126 | * Demonstrates running a request as a user using {@link WithMockUser}.
127 | *
128 | *
129 | * - The default username is "user"
130 | * - The default role is "ROLE_USER"
131 | * - The user does NOT need to exist
132 | *
133 | */
134 | @WithMockUser
135 | @Test
136 | public void testAuthorizedAccessWithMockUser() throws Exception {
137 | mockMvc
138 | .perform(get("/api/health"))
139 | .andExpect(status().isOk());
140 | }
141 |
142 | /**
143 | * Demonstrates requesting a protected page with invalid http basic credentials
144 | */
145 | @Test
146 | public void testUnauthorizedAccess() throws Exception {
147 | mockMvc
148 | .perform(get("/api/health").with(httpBasic("user", "invalid")))
149 | .andExpect(status().isUnauthorized());
150 | }
151 |
152 | }
153 |
--------------------------------------------------------------------------------
/spring-security/src/test/java/security/WebSecurityTests.java:
--------------------------------------------------------------------------------
1 | package security;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.boot.test.IntegrationTest;
7 | import org.springframework.boot.test.SpringApplicationConfiguration;
8 | import org.springframework.boot.test.TestRestTemplate;
9 | import org.springframework.http.*;
10 | import org.springframework.test.annotation.DirtiesContext;
11 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 | import org.springframework.test.context.web.WebAppConfiguration;
13 | import org.springframework.util.LinkedMultiValueMap;
14 | import org.springframework.util.MultiValueMap;
15 |
16 | import java.util.Collections;
17 | import java.util.List;
18 |
19 | import static org.junit.Assert.assertEquals;
20 | import static org.junit.Assert.assertTrue;
21 |
22 | /**
23 | * Basic integration tests for demo application.
24 | *
25 | * @author Dave Syer
26 | */
27 | @RunWith(SpringJUnit4ClassRunner.class)
28 | @SpringApplicationConfiguration(classes = {Application.class})
29 | @WebAppConfiguration
30 | @IntegrationTest({"server.port:0", "server.ssl.enabled:false"})
31 | @DirtiesContext
32 | public class WebSecurityTests {
33 |
34 | @Value("${local.server.port}")
35 | private int port;
36 |
37 | @Test
38 | public void testHome() throws Exception {
39 | HttpHeaders headers = new HttpHeaders();
40 | headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
41 | ResponseEntity entity = new TestRestTemplate().exchange(
42 | "http://localhost:" + this.port, HttpMethod.GET, new HttpEntity(
43 | headers), String.class);
44 | assertEquals(HttpStatus.OK, entity.getStatusCode());
45 | assertTrue("Wrong body (title doesn't match):\n" + entity.getBody(), entity
46 | .getBody().contains("Spring"));
47 | }
48 |
49 | @Test
50 | public void testLogin() throws Exception {
51 | HttpHeaders headers = new HttpHeaders();
52 | headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
53 | MultiValueMap form = new LinkedMultiValueMap<>();
54 | form.set("username", "user");
55 | form.set("password", "password");
56 | form.set("remember-me", "true");
57 | getCsrf(form, headers);
58 | ResponseEntity entity = new TestRestTemplate().exchange(
59 | "http://localhost:" + this.port + "/login", HttpMethod.POST,
60 | new HttpEntity<>(form, headers),
61 | String.class);
62 | assertEquals(HttpStatus.FOUND, entity.getStatusCode());
63 | List cookies = entity.getHeaders().get("Set-Cookie");
64 | assertTrue(cookies.toString().contains("remember-me"));
65 | assertEquals("http://localhost:" + this.port + "/", entity.getHeaders()
66 | .getLocation().toString());
67 | }
68 |
69 | @Test
70 | public void testDenied() throws Exception {
71 | HttpHeaders headers = new HttpHeaders();
72 | headers.setAccept(Collections.singletonList(MediaType.TEXT_HTML));
73 | MultiValueMap form = new LinkedMultiValueMap<>();
74 | form.set("username", "admin");
75 | form.set("password", "admin");
76 | getCsrf(form, headers);
77 | ResponseEntity entity = new TestRestTemplate().exchange(
78 | "http://localhost:" + this.port + "/login", HttpMethod.POST,
79 | new HttpEntity<>(form, headers),
80 | String.class);
81 | assertEquals(HttpStatus.FOUND, entity.getStatusCode());
82 | String cookie = entity.getHeaders().getFirst("Set-Cookie");
83 | headers.set("Cookie", cookie);
84 | ResponseEntity page = new TestRestTemplate().exchange(entity.getHeaders()
85 | .getLocation(), HttpMethod.GET, new HttpEntity(headers),
86 | String.class);
87 | assertEquals(HttpStatus.OK, page.getStatusCode());
88 | cookie = entity.getHeaders().getFirst("Set-Cookie");
89 | assertTrue(cookie.contains("remember-me"));
90 | assertTrue("Wrong body (message doesn't match):\n" + entity.getBody(), page
91 | .getBody().contains("Invalid username and password"));
92 | }
93 |
94 | @Test
95 | public void testProtected() throws Exception {
96 | HttpHeaders headers = new HttpHeaders();
97 | headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
98 | ResponseEntity entity = new TestRestTemplate().exchange(
99 | "http://localhost:" + this.port + "/api/health", HttpMethod.GET,
100 | new HttpEntity<>(headers),
101 | String.class);
102 | assertEquals(HttpStatus.UNAUTHORIZED, entity.getStatusCode());
103 | }
104 |
105 | @Test
106 | public void testAuthorizedAccess() throws Exception {
107 | ResponseEntity entity = new TestRestTemplate("user", "password")
108 | .getForEntity("http://localhost:" + this.port + "/api/health", String.class);
109 | assertEquals(HttpStatus.OK, entity.getStatusCode());
110 | }
111 |
112 | @Test
113 | public void testUnauthorizedAccess() throws Exception {
114 | ResponseEntity entity = new TestRestTemplate("admin", "admin")
115 | .getForEntity("http://localhost:" + this.port + "/api/health", String.class);
116 | assertEquals(HttpStatus.UNAUTHORIZED, entity.getStatusCode());
117 | }
118 |
119 | private void getCsrf(MultiValueMap form, HttpHeaders headers) {
120 | ResponseEntity extends CsrfToken> page = new TestRestTemplate().getForEntity(
121 | "http://localhost:" + this.port + "/api/csrf", CsrfToken.class);
122 | String cookie = page.getHeaders().getFirst("Set-Cookie");
123 | headers.set("Cookie", cookie);
124 | CsrfToken token = page.getBody();
125 | form.set(token.parameterName, token.token);
126 | }
127 |
128 | static class CsrfToken {
129 | private String parameterName;
130 | private String token;
131 |
132 | public String getParameterName() {
133 | return parameterName;
134 | }
135 | public void setParameterName(String parameterName) {
136 | this.parameterName = parameterName;
137 | }
138 | public String getToken() {
139 | return token;
140 | }
141 | public void setToken(String token) {
142 | this.token = token;
143 | }
144 | }
145 |
146 | }
147 |
--------------------------------------------------------------------------------