├── .gitignore
├── README.md
├── pom.xml
├── renovate.json
└── src
└── main
├── java
└── org
│ └── pac4j
│ └── demo
│ └── spring
│ ├── Application.java
│ ├── CustomAuthorizer.java
│ ├── MyErrorController.java
│ ├── Pac4jConfig.java
│ ├── SecurityConfig.java
│ └── SpringBootPac4jDemo.java
└── resources
├── application.properties
├── metadata-okta.xml
├── samlKeystore.jks
├── templates
├── error401.html
├── error403.html
├── error500.html
├── form.html
├── index.html
├── jwt.html
├── notProtected.html
└── protectedIndex.html
└── testshib-providers.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | .classpath
2 | .project
3 | .settings/
4 | target/
5 | test-output/
6 | bin/
7 | *.iml
8 | .idea
9 | sp-metadata.xml
10 | .DS_Store
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | This `spring-webmvc-pac4j-boot-demo` project is a Spring Boot application secured by the [spring-webmvc-pac4j](https://github.com/pac4j/spring-webmvc-pac4j) security library.
6 |
7 | ## Run and test
8 |
9 | You can build the project and run it on [http://localhost:8080](http://localhost:8080) using the following commands:
10 |
11 | cd spring-webmvc-pac4j-boot-demo
12 | mvn clean compile exec:java
13 |
14 | or
15 |
16 | cd spring-webmvc-pac4j-boot-demo
17 | mvn spring-boot:run
18 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 | org.pac4j.demo
4 | spring-webmvc-pac4j-boot-demo
5 | 8.0.0-SNAPSHOT
6 | jar
7 | spring-webmvc-pac4j demo for Spring boot webapp
8 |
9 |
10 |
11 | sonatype-nexus-snapshots
12 | Sonatype Nexus Snapshots
13 | https://oss.sonatype.org/content/repositories/snapshots
14 |
15 | false
16 |
17 |
18 | true
19 |
20 |
21 |
22 | spring-milestone
23 | Spring Milestone Repository
24 | https://repo.spring.io/milestone
25 |
26 |
27 |
28 |
29 | spring-milestones
30 | https://repo.spring.io/milestone
31 |
32 |
33 |
34 |
35 | org.springframework.boot
36 | spring-boot-starter-parent
37 | 3.5.0
38 |
39 |
40 |
41 | 17
42 | 8.0.1
43 | 6.1.3
44 |
45 |
46 |
47 |
48 | org.springframework.boot
49 | spring-boot-starter-web
50 |
51 |
52 | org.springframework.boot
53 | spring-boot-starter-mustache
54 |
55 |
56 | org.pac4j
57 | spring-webmvc-pac4j
58 | ${spring-webmvc-pac4j.version}
59 |
60 |
61 | org.pac4j
62 | pac4j-jakartaee
63 | ${pac4j.version}
64 |
65 |
66 | org.pac4j
67 | pac4j-oauth
68 | ${pac4j.version}
69 |
70 |
71 | org.pac4j
72 | pac4j-cas
73 | ${pac4j.version}
74 |
75 |
76 | org.pac4j
77 | pac4j-saml
78 | ${pac4j.version}
79 |
80 |
81 | org.pac4j
82 | pac4j-gae
83 | ${pac4j.version}
84 |
85 |
86 | org.pac4j
87 | pac4j-oidc
88 | ${pac4j.version}
89 |
90 |
91 | org.pac4j
92 | pac4j-http
93 | ${pac4j.version}
94 |
95 |
96 | org.pac4j
97 | pac4j-ldap
98 | ${pac4j.version}
99 |
100 |
101 | org.pac4j
102 | pac4j-jwt
103 | ${pac4j.version}
104 |
105 |
106 | org.pac4j
107 | pac4j-sql
108 | ${pac4j.version}
109 |
110 |
111 | org.pac4j
112 | pac4j-mongo
113 | ${pac4j.version}
114 |
115 |
116 | org.pac4j
117 | pac4j-kerberos
118 | ${pac4j.version}
119 |
120 |
121 | org.pac4j
122 | pac4j-couch
123 | ${pac4j.version}
124 |
125 |
126 |
127 | ${project.artifactId}
128 |
129 |
130 | org.springframework.boot
131 | spring-boot-maven-plugin
132 |
133 |
134 | org.apache.maven.plugins
135 | maven-compiler-plugin
136 | 3.14.0
137 |
138 | ${java.version}
139 | UTF-8
140 |
141 |
142 |
143 | org.codehaus.mojo
144 | exec-maven-plugin
145 | 3.5.1
146 |
147 |
148 |
149 | java
150 |
151 |
152 |
153 |
154 | org.pac4j.demo.spring.SpringBootPac4jDemo
155 |
156 |
157 |
158 |
159 |
160 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/src/main/java/org/pac4j/demo/spring/Application.java:
--------------------------------------------------------------------------------
1 | package org.pac4j.demo.spring;
2 |
3 | import org.pac4j.core.client.Client;
4 | import org.pac4j.core.config.Config;
5 | import org.pac4j.core.context.CallContext;
6 | import org.pac4j.core.exception.http.HttpAction;
7 | import org.pac4j.core.profile.ProfileManager;
8 | import org.pac4j.core.profile.UserProfile;
9 | import org.pac4j.core.util.Pac4jConstants;
10 | import org.pac4j.http.client.indirect.FormClient;
11 | import org.pac4j.jee.context.JEEContext;
12 | import org.pac4j.jee.context.session.JEESessionStore;
13 | import org.pac4j.jee.http.adapter.JEEHttpActionAdapter;
14 | import org.pac4j.jwt.config.encryption.SecretEncryptionConfiguration;
15 | import org.pac4j.jwt.config.signature.SecretSignatureConfiguration;
16 | import org.pac4j.jwt.profile.JwtGenerator;
17 | import org.pac4j.springframework.annotation.RequireAnyRole;
18 | import org.pac4j.springframework.web.LogoutController;
19 | import org.springframework.beans.factory.annotation.Autowired;
20 | import org.springframework.beans.factory.annotation.Value;
21 | import org.springframework.stereotype.Controller;
22 | import org.springframework.web.bind.annotation.ExceptionHandler;
23 | import org.springframework.web.bind.annotation.RequestMapping;
24 | import org.springframework.web.bind.annotation.ResponseBody;
25 |
26 | import javax.annotation.PostConstruct;
27 | import java.util.Map;
28 | import java.util.Optional;
29 |
30 | @Controller
31 | public class Application {
32 |
33 | @Value("${salt}")
34 | private String salt;
35 |
36 | @Value("${pac4j.centralLogout.defaultUrl:#{null}}")
37 | private String defaultUrl;
38 |
39 | @Value("${pac4j.centralLogout.logoutUrlPattern:#{null}}")
40 | private String logoutUrlPattern;
41 |
42 | @Autowired
43 | private Config config;
44 |
45 | @Autowired
46 | private JEEContext webContext;
47 |
48 | @Autowired
49 | private ProfileManager profileManager;
50 |
51 | private LogoutController logoutController;
52 |
53 | @PostConstruct
54 | protected void afterPropertiesSet() {
55 | logoutController = new LogoutController();
56 | logoutController.setDefaultUrl(defaultUrl);
57 | logoutController.setLogoutUrlPattern(logoutUrlPattern);
58 | logoutController.setLocalLogout(false);
59 | logoutController.setCentralLogout(true);
60 | logoutController.setConfig(config);
61 | logoutController.setDestroySession(false);
62 | }
63 |
64 | @RequestMapping("/")
65 | public String root(final Map map) throws HttpAction {
66 | return index(map);
67 | }
68 |
69 | @RequestMapping("/index.html")
70 | public String index(final Map map) throws HttpAction {
71 | map.put("profiles", profileManager.getProfiles());
72 | map.put("sessionId", new JEESessionStore().getSessionId(webContext, false).orElse("nosession"));
73 | return "index";
74 | }
75 |
76 | @RequestMapping("/facebook/index.html")
77 | public String facebook(final Map map) {
78 | return protectedIndex(map);
79 | }
80 |
81 | @RequestMapping("/facebook/notprotected.html")
82 | public String facebookNotProtected(final Map map) {
83 | map.put("profiles", profileManager.getProfiles());
84 | return "notProtected";
85 | }
86 |
87 | @RequestMapping("/facebookadmin/index.html")
88 | @RequireAnyRole("ROLE_ADMIN")
89 | public String facebookadmin(final Map map) {
90 | return protectedIndex(map);
91 | }
92 |
93 | @RequestMapping("/facebookcustom/index.html")
94 | public String facebookcustom(final Map map) {
95 | return protectedIndex(map);
96 | }
97 |
98 | @RequestMapping("/twitter/index.html")
99 | public String twitter(final Map map) {
100 | return protectedIndex(map);
101 | }
102 |
103 | @RequestMapping("/form/index.html")
104 | public String form(final Map map) {
105 | return protectedIndex(map);
106 | }
107 |
108 | @RequestMapping("/basicauth/index.html")
109 | public String basicauth(final Map map) {
110 | return protectedIndex(map);
111 | }
112 |
113 | @RequestMapping("/cas/index.html")
114 | public String cas(final Map map) {
115 | return protectedIndex(map);
116 | }
117 |
118 | @RequestMapping("/saml/index.html")
119 | public String saml(final Map map) {
120 | return protectedIndex(map);
121 | }
122 |
123 | @RequestMapping("/oidc/index.html")
124 | public String oidc(final Map map) {
125 | return protectedIndex(map);
126 | }
127 |
128 | @RequestMapping("/protected/index.html")
129 | public String protect(final Map map) {
130 | return protectedIndex(map);
131 | }
132 |
133 | @RequestMapping("/loginForm")
134 | public String loginForm(final Map map) {
135 | final FormClient formClient = (FormClient) config.getClients().findClient("FormClient").get();
136 | map.put("callbackUrl", formClient.getCallbackUrl());
137 | return "form";
138 | }
139 |
140 | @RequestMapping("/forceLogin")
141 | @ResponseBody
142 | public void forceLogin() {
143 | try {
144 | final String name = webContext.getRequestParameter(Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER)
145 | .map(String::valueOf).orElse("");
146 | final Client client = config.getClients().findClient(name).get();
147 | JEEHttpActionAdapter.INSTANCE.adapt(client.getRedirectionAction(new CallContext(webContext, new JEESessionStore())).get(), webContext);
148 | } catch (final HttpAction e) {
149 | }
150 | }
151 |
152 | protected String protectedIndex(final Map map) {
153 | map.put("profiles", profileManager.getProfiles());
154 | return "protectedIndex";
155 | }
156 |
157 | @RequestMapping("/centralLogout")
158 | @ResponseBody
159 | public void centralLogout() {
160 | logoutController.logout(webContext.getNativeRequest(), webContext.getNativeResponse());
161 | }
162 |
163 | @RequestMapping("/dba/index.html")
164 | public String dba(final Map map) {
165 | return protectedIndex(map);
166 | }
167 |
168 | @RequestMapping("/rest-jwt/index.html")
169 | public String restJwt(final Map map) {
170 | return protectedIndex(map);
171 | }
172 |
173 | @RequestMapping("/casrest/index.html")
174 | public String casrest(final Map map) {
175 | return protectedIndex(map);
176 | }
177 |
178 | @RequestMapping("/jwt.html")
179 | public String jwt(final Map map) {
180 | final SecretSignatureConfiguration secretSignatureConfiguration = new SecretSignatureConfiguration(salt);
181 | final SecretEncryptionConfiguration secretEncryptionConfiguration = new SecretEncryptionConfiguration(salt);
182 | final JwtGenerator generator = new JwtGenerator();
183 | generator.setSignatureConfiguration(secretSignatureConfiguration);
184 | generator.setEncryptionConfiguration(secretEncryptionConfiguration);
185 | String token = "";
186 | // by default, as we are in a REST API controller, profiles are retrieved only in the request
187 | // here, we retrieve the profile from the session as we generate the token from a profile saved by an indirect client (from the UserInterfaceApplication)
188 | final Optional profile = profileManager.getProfile();
189 | if (profile.isPresent()) {
190 | token = generator.generate(profile.get());
191 | }
192 | map.put("token", token);
193 | return "jwt";
194 | }
195 |
196 | @ExceptionHandler(HttpAction.class)
197 | public void httpAction() {
198 | // do nothing
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/src/main/java/org/pac4j/demo/spring/CustomAuthorizer.java:
--------------------------------------------------------------------------------
1 | package org.pac4j.demo.spring;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 | import org.pac4j.core.authorization.authorizer.ProfileAuthorizer;
5 | import org.pac4j.core.context.WebContext;
6 | import org.pac4j.core.context.session.SessionStore;
7 | import org.pac4j.core.profile.UserProfile;
8 |
9 | import java.util.List;
10 |
11 | public class CustomAuthorizer extends ProfileAuthorizer {
12 |
13 | @Override
14 | public boolean isAuthorized(final WebContext context, final SessionStore sessionStore, final List profiles) {
15 | return isAnyAuthorized(context, sessionStore, profiles);
16 | }
17 |
18 | @Override
19 | public boolean isProfileAuthorized(final WebContext context, final SessionStore sessionStore, final UserProfile profile) {
20 | if (profile == null) {
21 | return false;
22 | }
23 | return StringUtils.startsWith(profile.getUsername(), "jle");
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/org/pac4j/demo/spring/MyErrorController.java:
--------------------------------------------------------------------------------
1 | package org.pac4j.demo.spring;
2 |
3 | import jakarta.servlet.http.HttpServletRequest;
4 | import jakarta.servlet.http.HttpServletResponse;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.boot.autoconfigure.web.ErrorProperties;
7 | import org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController;
8 | import org.springframework.boot.web.servlet.error.ErrorAttributes;
9 | import org.springframework.http.HttpStatus;
10 | import org.springframework.stereotype.Controller;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 | import org.springframework.web.servlet.ModelAndView;
13 |
14 | @Controller
15 | @RequestMapping({"${server.error.path:${error.path:/error}}"})
16 | public class MyErrorController extends BasicErrorController {
17 |
18 | @Autowired
19 | public MyErrorController(ErrorAttributes errorAttributes) {
20 | super(errorAttributes, new ErrorProperties());
21 | }
22 |
23 | @RequestMapping(
24 | produces = {"text/html"}
25 | )
26 | @Override
27 | public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
28 | final HttpStatus status = getStatus(request);
29 | if (status.equals(HttpStatus.UNAUTHORIZED)) {
30 | return new ModelAndView("error401");
31 | } else if (status.equals(HttpStatus.FORBIDDEN)) {
32 | return new ModelAndView("error403");
33 | } else {
34 | return new ModelAndView("error500");
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/org/pac4j/demo/spring/Pac4jConfig.java:
--------------------------------------------------------------------------------
1 | package org.pac4j.demo.spring;
2 |
3 | import com.nimbusds.jose.JWSAlgorithm;
4 | import org.pac4j.cas.client.CasClient;
5 | import org.pac4j.cas.config.CasConfiguration;
6 | import org.pac4j.core.client.Clients;
7 | import org.pac4j.core.client.direct.AnonymousClient;
8 | import org.pac4j.core.config.Config;
9 | import org.pac4j.http.client.direct.DirectBasicAuthClient;
10 | import org.pac4j.http.client.direct.ParameterClient;
11 | import org.pac4j.http.client.indirect.FormClient;
12 | import org.pac4j.http.client.indirect.IndirectBasicAuthClient;
13 | import org.pac4j.http.credentials.authenticator.test.SimpleTestUsernamePasswordAuthenticator;
14 | import org.pac4j.jwt.config.encryption.SecretEncryptionConfiguration;
15 | import org.pac4j.jwt.config.signature.SecretSignatureConfiguration;
16 | import org.pac4j.jwt.credentials.authenticator.JwtAuthenticator;
17 | import org.pac4j.oauth.client.FacebookClient;
18 | import org.pac4j.oauth.client.TwitterClient;
19 | import org.pac4j.oidc.client.GoogleOidcClient;
20 | import org.pac4j.oidc.config.OidcConfiguration;
21 | import org.pac4j.saml.client.SAML2Client;
22 | import org.pac4j.saml.config.SAML2Configuration;
23 | import org.springframework.beans.factory.annotation.Value;
24 | import org.springframework.context.annotation.Bean;
25 | import org.springframework.context.annotation.Configuration;
26 | import org.springframework.core.io.ClassPathResource;
27 | import org.springframework.core.io.FileSystemResource;
28 |
29 | import java.io.File;
30 | import java.util.Optional;
31 |
32 | @Configuration
33 | public class Pac4jConfig {
34 |
35 | @Value("${salt}")
36 | private String salt;
37 |
38 | @Bean
39 | public Config config() {
40 | /*final OidcConfiguration oidcConfiguration = new OidcConfiguration();
41 | oidcConfiguration.setClientId("test");
42 | oidcConfiguration.setSecret("secret");
43 | oidcConfiguration.setUseNonce(true);
44 | oidcConfiguration.setDiscoveryURI("http://localhost:5000/.well-known/openid-configuration");
45 | oidcConfiguration.setScope("openid api1");
46 | oidcConfiguration.setClientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC);
47 | oidcConfiguration.addCustomParam("prompt", "consent");
48 | final OidcClient oidcClient = new OidcClient(oidcConfiguration);
49 | oidcClient.setName("test");
50 | oidcClient.setCallbackUrl("http://localhost:8080/callback");*/
51 |
52 | final OidcConfiguration oidcConfiguration = new OidcConfiguration();
53 | oidcConfiguration.setClientId("167480702619-8e1lo80dnu8bpk3k0lvvj27noin97vu9.apps.googleusercontent.com");
54 | oidcConfiguration.setSecret("MhMme_Ik6IH2JMnAT6MFIfee");
55 | oidcConfiguration.setPreferredJwsAlgorithm(JWSAlgorithm.PS384);
56 | oidcConfiguration.addCustomParam("prompt", "consent");
57 | final GoogleOidcClient oidcClient = new GoogleOidcClient(oidcConfiguration);
58 | oidcClient.setAuthorizationGenerator((ctx, profile) -> {
59 | profile.addRole("ROLE_ADMIN");
60 | return Optional.of(profile);
61 | });
62 |
63 | final SAML2Configuration cfg = new SAML2Configuration(new ClassPathResource("samlKeystore.jks"),
64 | "pac4j-demo-passwd",
65 | "pac4j-demo-passwd",
66 | new ClassPathResource("metadata-okta.xml"));
67 | cfg.setMaximumAuthenticationLifetime(3600);
68 | cfg.setServiceProviderEntityId("http://localhost:8080/callback?client_name=SAML2Client");
69 | cfg.setServiceProviderMetadataResource(new FileSystemResource(new File("sp-metadata.xml").getAbsoluteFile()));
70 | final SAML2Client saml2Client = new SAML2Client(cfg);
71 |
72 | final FacebookClient facebookClient = new FacebookClient("145278422258960", "be21409ba8f39b5dae2a7de525484da8");
73 | facebookClient.setMultiProfile(true);
74 | final TwitterClient twitterClient = new TwitterClient("CoxUiYwQOSFDReZYdjigBA",
75 | "2kAzunH5Btc4gRSaMr7D7MkyoJ5u1VzbOOzE8rBofs");
76 | // HTTP
77 | final FormClient formClient = new FormClient("http://localhost:8080/loginForm", new SimpleTestUsernamePasswordAuthenticator());
78 | final IndirectBasicAuthClient indirectBasicAuthClient = new IndirectBasicAuthClient(new SimpleTestUsernamePasswordAuthenticator());
79 |
80 | // CAS
81 | final CasConfiguration configuration = new CasConfiguration("https://casserverpac4j.herokuapp.com/login");
82 | final CasClient casClient = new CasClient(configuration);
83 | // casClient.setGateway(true);
84 |
85 | // REST authent with JWT for a token passed in the url as the token parameter
86 | final SecretSignatureConfiguration secretSignatureConfiguration = new SecretSignatureConfiguration(salt);
87 | final SecretEncryptionConfiguration secretEncryptionConfiguration = new SecretEncryptionConfiguration(salt);
88 | final JwtAuthenticator authenticator = new JwtAuthenticator();
89 | authenticator.setSignatureConfiguration(secretSignatureConfiguration);
90 | authenticator.setEncryptionConfiguration(secretEncryptionConfiguration);
91 | final ParameterClient parameterClient = new ParameterClient("token", authenticator);
92 | parameterClient.setSupportGetRequest(true);
93 | parameterClient.setSupportPostRequest(false);
94 |
95 | // basic auth
96 | final DirectBasicAuthClient directBasicAuthClient = new DirectBasicAuthClient(new SimpleTestUsernamePasswordAuthenticator());
97 |
98 | final Clients clients = new Clients("http://localhost:8080/callback", oidcClient, saml2Client, facebookClient,
99 | twitterClient, formClient, indirectBasicAuthClient, casClient, parameterClient, directBasicAuthClient, new AnonymousClient());
100 |
101 | return new Config(clients);
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/org/pac4j/demo/spring/SecurityConfig.java:
--------------------------------------------------------------------------------
1 | package org.pac4j.demo.spring;
2 |
3 | import org.pac4j.core.authorization.authorizer.RequireAnyRoleAuthorizer;
4 | import org.pac4j.core.config.Config;
5 | import org.pac4j.springframework.annotation.AnnotationConfig;
6 | import org.pac4j.springframework.component.ComponentConfig;
7 | import org.pac4j.springframework.web.SecurityInterceptor;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.context.annotation.ComponentScan;
10 | import org.springframework.context.annotation.Configuration;
11 | import org.springframework.context.annotation.Import;
12 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
13 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
14 |
15 | @Configuration
16 | @Import({ComponentConfig.class, AnnotationConfig.class})
17 | @ComponentScan(basePackages = "org.pac4j.springframework.web")
18 | public class SecurityConfig implements WebMvcConfigurer {
19 |
20 | @Autowired
21 | private Config config;
22 |
23 | @Override
24 | public void addInterceptors(InterceptorRegistry registry) {
25 | registry.addInterceptor(buildInterceptor("FacebookClient"))
26 | .addPathPatterns("/facebook/*")
27 | .excludePathPatterns("/facebook/notprotected.html");
28 |
29 | SecurityInterceptor interceptor = SecurityInterceptor.build(config,
30 | "FacebookClient", new RequireAnyRoleAuthorizer("ROLE_ADMIN"));
31 |
32 | registry.addInterceptor(interceptor).addPathPatterns("/facebookadmin/*");
33 | registry.addInterceptor(buildInterceptor("FacebookClient")).addPathPatterns("/facebookadmin/*");
34 |
35 | interceptor = SecurityInterceptor.build(config,
36 | "FacebookClient", new CustomAuthorizer());
37 |
38 | registry.addInterceptor(interceptor).addPathPatterns("/facebookcustom/*");
39 | registry.addInterceptor(buildInterceptor("TwitterClient,FacebookClient")).addPathPatterns("/twitter/*");
40 | registry.addInterceptor(buildInterceptor("FormClient")).addPathPatterns("/form/*");
41 | registry.addInterceptor(buildInterceptor("IndirectBasicAuthClient")).addPathPatterns("/basicauth/*");
42 | registry.addInterceptor(buildInterceptor("CasClient")).addPathPatterns("/cas/*");
43 | registry.addInterceptor(buildInterceptor("SAML2Client")).addPathPatterns("/saml/*");
44 | registry.addInterceptor(buildInterceptor("GoogleOidcClient")).addPathPatterns("/oidc/*");
45 | registry.addInterceptor(new SecurityInterceptor(config)).addPathPatterns("/protected/*");
46 | registry.addInterceptor(buildInterceptor("DirectBasicAuthClient,ParameterClient")).addPathPatterns("/dba/*");
47 | registry.addInterceptor(buildInterceptor("ParameterClient")).addPathPatterns("/rest-jwt/*");
48 |
49 | registry.addInterceptor(buildInterceptor("AnonymousClient")).addPathPatterns("/*").excludePathPatterns("/callback*");
50 | }
51 |
52 | private SecurityInterceptor buildInterceptor(final String client) {
53 | return SecurityInterceptor.build(config, client);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/org/pac4j/demo/spring/SpringBootPac4jDemo.java:
--------------------------------------------------------------------------------
1 | package org.pac4j.demo.spring;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
6 | import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
7 |
8 | @SpringBootApplication(exclude = {
9 | MongoAutoConfiguration.class,
10 | MongoDataAutoConfiguration.class
11 | })
12 | public class SpringBootPac4jDemo {
13 |
14 | public static void main(final String[] args) {
15 | SpringApplication.run(SpringBootPac4jDemo.class, args);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | salt=12345678901234567890123456789012
2 |
3 | pac4j.logout.defaultUrl=/?defaulturlafterlogout
4 | pac4j.logout.destroySession=true
5 |
6 | pac4j.centralLogout.defaultUrl=http://localhost:8080/?defaulturlafterlogoutafteridp
7 | pac4j.centralLogout.logoutUrlPattern=http://localhost:8080/.*
8 |
9 | spring.mustache.suffix=.html
10 |
11 | logging.level.org.pac4j.core.engine=DEBUG
12 |
--------------------------------------------------------------------------------
/src/main/resources/metadata-okta.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | MIIDpDCCAoygAwIBAgIGAVFtfuF5MA0GCSqGSIb3DQEBBQUAMIGSMQswCQYDVQQGEwJVUzETMBEG
9 | A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU
10 | MBIGA1UECwwLU1NPUHJvdmlkZXIxEzARBgNVBAMMCmRldi00MjU5NTQxHDAaBgkqhkiG9w0BCQEW
11 | DWluZm9Ab2t0YS5jb20wHhcNMTUxMjA0MTQ1NTUwWhcNMjUxMjA0MTQ1NjUwWjCBkjELMAkGA1UE
12 | BhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xDTALBgNV
13 | BAoMBE9rdGExFDASBgNVBAsMC1NTT1Byb3ZpZGVyMRMwEQYDVQQDDApkZXYtNDI1OTU0MRwwGgYJ
14 | KoZIhvcNAQkBFg1pbmZvQG9rdGEuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
15 | gtbqJHZYUwLGA9zb4OX2Xdb/2+8gIx8d9g6q3INMRxoPvElh7znRHLOpL8h0iEyhMyXfmgXkJf1U
16 | eUOWUZa0rFIKoY0NSqCSlX44ZuseVwUsDA/vtVq/FSZ5/RAU/iMfbvWfCITVoLsjLHKr+3cnaN/0
17 | 6coe6mOtMJGqWoN/EeH+3lwyFDuk0vbxGqlrn/aXlHLWaSyFJ4CnMU/y0gxiF7kDXFGrj44fkDV9
18 | MJ8k7MjM6WDC5b9eBcfjCCSSR0DZ+0XeGi8VsewRNvmmlGvMuNJJv0TaJNBtOCP4kEClHQIyaBQZ
19 | X3wXi9XiQNwofQTj4qoJ1fHriGVwhS7UI8Xe3wIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQB19OcZ
20 | j6x0wVOhjSXTPbJtC1m1E01lgk3+kHzGAk+U5JsYDhEtEOZ2cZ7XIAYjab4RcdAd4hrRbPcupfIB
21 | dHLBeBnWur6C7DpN1mZbfxwvDp1d60wRi1A5PmcrewoQDuQSI3zrIhb+FcqtCewl7Ku1pNX0Nng4
22 | NL95pwiYkOoqfoBTz1WOFq+dsAygPoyneHIARyftdbFQpHmlN+/RRudH6WqAQJ1mXYP9+8tv+yuY
23 | jhy0VURe5ZkFEIO3WtxFxMBj6Z8L1edngYVj3f8t8FmuFxYCqHw0Ex98XLMd0XlL06z4qmE4uJdg
24 | VlDGyllCF+le88VWtD7AB+QxoA/nZxMs
25 |
26 |
27 |
28 |
29 |
30 | urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
31 |
32 |
33 | urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/main/resources/samlKeystore.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pac4j/spring-webmvc-pac4j-boot-demo/81eaa019b6eb8038333af353b761a47737ace462/src/main/resources/samlKeystore.jks
--------------------------------------------------------------------------------
/src/main/resources/templates/error401.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | unauthorized
4 |
5 | Home
6 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/templates/error403.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | forbidden
4 |
5 | Home
6 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/templates/error500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | internal error
4 |
5 | Home
6 |
7 |
--------------------------------------------------------------------------------
/src/main/resources/templates/form.html:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/main/resources/templates/index.html:
--------------------------------------------------------------------------------
1 | index
2 | Protected url by Facebook: facebook/index.html (use a real account)
3 | Not protected page: facebook/notprotected.html (no authentication required)
4 | Protected url by Facebook with ROLE_ADMIN: facebookadmin/index.html (use a real account)
5 | Protected url by Facebook with custom authorizer (= must be a CommonProfile where the username starts with "jle"): facebookcustom/index.html (login with form or basic authentication before with jle* username)
6 | Protected url by Twitter: twitter/index.html or by Facebook: twitter/index.html?force_client=FacebookClient (use a real account)
7 | Protected url by form authentication: form/index.html (use login = pwd)
8 | Protected url by indirect basic auth: basicauth/index.html (use login = pwd)
9 | Protected url by CAS: cas/index.html (use login = pwd)
10 | Protected url by SAML2: saml/index.html (use testpac4j at gmail.com / Pac4jtest)
11 | Protected url by Google OpenID Connect: oidc/index.html (use a real account)
12 | Protected url: protected/index.html
13 |
14 | Generate a JWT token (won't start any login process)
15 | Protected url by DirectBasicAuthClient: /dba/index.html (POST the Authorization header with value: Basic amxlbGV1OmpsZWxldQ==) then by ParameterClient: /dba/index.html (with request parameter: token=jwt_generated_token)
16 | Protected url by ParameterClient: /rest-jwt/index.html (with request parameter: token=jwt_generated_token)
17 |
18 | Force Facebook login (even if already authenticated)
19 |
20 | local logout
21 |
22 | central logout
23 |
24 | profiles: {{profiles}}
25 |
26 | sessionId: {{sessionId}}
27 |
--------------------------------------------------------------------------------
/src/main/resources/templates/jwt.html:
--------------------------------------------------------------------------------
1 | Generate JWT token
2 | Back
3 |
4 | token: {{token}}
5 |
--------------------------------------------------------------------------------
/src/main/resources/templates/notProtected.html:
--------------------------------------------------------------------------------
1 | Not protected page
2 | Back
3 |
4 | profiles: {{profiles}}
5 |
--------------------------------------------------------------------------------
/src/main/resources/templates/protectedIndex.html:
--------------------------------------------------------------------------------
1 | protected area
2 | Back
3 |
4 | profiles: {{profiles}}
5 |
--------------------------------------------------------------------------------
/src/main/resources/testshib-providers.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 | testshib.org
27 |
28 | TestShib Test IdP
29 | TestShib IdP. Use this as a source of attributes
30 | for your test SP.
31 | https://www.testshib.org/testshibtwo.jpg
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEV
41 | MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYD
42 | VQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4
43 | MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQI
44 | EwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRl
45 | c3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0B
46 | AQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7C
47 | yVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe
48 | 3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aT
49 | NPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614
50 | kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWH
51 | gWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0G
52 | A1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ86
53 | 9nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBl
54 | bm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNo
55 | aWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN
56 | BgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRL
57 | I4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo
58 | 93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4
59 | /SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAj
60 | Geka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr
61 | 8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
76 |
79 |
80 | urn:mace:shibboleth:1.0:nameIdentifier
81 | urn:oasis:names:tc:SAML:2.0:nameid-format:transient
82 |
83 |
85 |
87 |
89 |
91 |
92 |
93 |
94 |
95 |
97 |
98 |
99 |
100 |
101 |
102 | MIIEDjCCAvagAwIBAgIBADANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzEV
103 | MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMREwDwYD
104 | VQQKEwhUZXN0U2hpYjEZMBcGA1UEAxMQaWRwLnRlc3RzaGliLm9yZzAeFw0wNjA4
105 | MzAyMTEyMjVaFw0xNjA4MjcyMTEyMjVaMGcxCzAJBgNVBAYTAlVTMRUwEwYDVQQI
106 | EwxQZW5uc3lsdmFuaWExEzARBgNVBAcTClBpdHRzYnVyZ2gxETAPBgNVBAoTCFRl
107 | c3RTaGliMRkwFwYDVQQDExBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0B
108 | AQEFAAOCAQ8AMIIBCgKCAQEArYkCGuTmJp9eAOSGHwRJo1SNatB5ZOKqDM9ysg7C
109 | yVTDClcpu93gSP10nH4gkCZOlnESNgttg0r+MqL8tfJC6ybddEFB3YBo8PZajKSe
110 | 3OQ01Ow3yT4I+Wdg1tsTpSge9gEz7SrC07EkYmHuPtd71CHiUaCWDv+xVfUQX0aT
111 | NPFmDixzUjoYzbGDrtAyCqA8f9CN2txIfJnpHE6q6CmKcoLADS4UrNPlhHSzd614
112 | kR/JYiks0K4kbRqCQF0Dv0P5Di+rEfefC6glV8ysC8dB5/9nb0yh/ojRuJGmgMWH
113 | gWk6h0ihjihqiu4jACovUZ7vVOCgSE5Ipn7OIwqd93zp2wIDAQABo4HEMIHBMB0G
114 | A1UdDgQWBBSsBQ869nh83KqZr5jArr4/7b+QazCBkQYDVR0jBIGJMIGGgBSsBQ86
115 | 9nh83KqZr5jArr4/7b+Qa6FrpGkwZzELMAkGA1UEBhMCVVMxFTATBgNVBAgTDFBl
116 | bm5zeWx2YW5pYTETMBEGA1UEBxMKUGl0dHNidXJnaDERMA8GA1UEChMIVGVzdFNo
117 | aWIxGTAXBgNVBAMTEGlkcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN
118 | BgkqhkiG9w0BAQUFAAOCAQEAjR29PhrCbk8qLN5MFfSVk98t3CT9jHZoYxd8QMRL
119 | I4j7iYQxXiGJTT1FXs1nd4Rha9un+LqTfeMMYqISdDDI6tv8iNpkOAvZZUosVkUo
120 | 93pv1T0RPz35hcHHYq2yee59HJOco2bFlcsH8JBXRSRrJ3Q7Eut+z9uo80JdGNJ4
121 | /SJy5UorZ8KazGj16lfJhOBXldgrhppQBb0Nq6HKHguqmwRfJ+WkxemZXzhediAj
122 | Geka8nz8JjwxpUjAiSWYKLtJhGEaTqCYxCCX2Dw+dOTqUzHOZ7WKv4JXPK5G/Uhr
123 | 8K/qhmFT2nIQi538n6rVYLeWj8Bbnl+ev0peYzxFyF5sQA==
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
138 |
140 |
141 | urn:mace:shibboleth:1.0:nameIdentifier
142 | urn:oasis:names:tc:SAML:2.0:nameid-format:transient
143 |
144 |
145 |
146 |
147 | TestShib Two Identity Provider
148 | TestShib Two
149 | http://www.testshib.org/testshib-two/
150 |
151 |
152 | Nate
153 | Klingenstein
154 | ndk@internet2.edu
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
174 |
175 |
176 |
177 |
181 |
182 |
183 | TestShib Test SP
184 | TestShib SP. Log into this to test your machine.
185 | Once logged in check that all attributes that you expected have been
186 | released.
187 | https://www.testshib.org/testshibtwo.jpg
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | MIIEPjCCAyagAwIBAgIBADANBgkqhkiG9w0BAQUFADB3MQswCQYDVQQGEwJVUzEV
196 | MBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1cmdoMSIwIAYD
197 | VQQKExlUZXN0U2hpYiBTZXJ2aWNlIFByb3ZpZGVyMRgwFgYDVQQDEw9zcC50ZXN0
198 | c2hpYi5vcmcwHhcNMDYwODMwMjEyNDM5WhcNMTYwODI3MjEyNDM5WjB3MQswCQYD
199 | VQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYDVQQHEwpQaXR0c2J1
200 | cmdoMSIwIAYDVQQKExlUZXN0U2hpYiBTZXJ2aWNlIFByb3ZpZGVyMRgwFgYDVQQD
201 | Ew9zcC50ZXN0c2hpYi5vcmcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
202 | AQDJyR6ZP6MXkQ9z6RRziT0AuCabDd3x1m7nLO9ZRPbr0v1LsU+nnC363jO8nGEq
203 | sqkgiZ/bSsO5lvjEt4ehff57ERio2Qk9cYw8XCgmYccVXKH9M+QVO1MQwErNobWb
204 | AjiVkuhWcwLWQwTDBowfKXI87SA7KR7sFUymNx5z1aoRvk3GM++tiPY6u4shy8c7
205 | vpWbVfisfTfvef/y+galxjPUQYHmegu7vCbjYP3On0V7/Ivzr+r2aPhp8egxt00Q
206 | XpilNai12LBYV3Nv/lMsUzBeB7+CdXRVjZOHGuQ8mGqEbsj8MBXvcxIKbcpeK5Zi
207 | JCVXPfarzuriM1G5y5QkKW+LAgMBAAGjgdQwgdEwHQYDVR0OBBYEFKB6wPDxwYrY
208 | StNjU5P4b4AjBVQVMIGhBgNVHSMEgZkwgZaAFKB6wPDxwYrYStNjU5P4b4AjBVQV
209 | oXukeTB3MQswCQYDVQQGEwJVUzEVMBMGA1UECBMMUGVubnN5bHZhbmlhMRMwEQYD
210 | VQQHEwpQaXR0c2J1cmdoMSIwIAYDVQQKExlUZXN0U2hpYiBTZXJ2aWNlIFByb3Zp
211 | ZGVyMRgwFgYDVQQDEw9zcC50ZXN0c2hpYi5vcmeCAQAwDAYDVR0TBAUwAwEB/zAN
212 | BgkqhkiG9w0BAQUFAAOCAQEAc06Kgt7ZP6g2TIZgMbFxg6vKwvDL0+2dzF11Onpl
213 | 5sbtkPaNIcj24lQ4vajCrrGKdzHXo9m54BzrdRJ7xDYtw0dbu37l1IZVmiZr12eE
214 | Iay/5YMU+aWP1z70h867ZQ7/7Y4HW345rdiS6EW663oH732wSYNt9kr7/0Uer3KD
215 | 9CuPuOidBacospDaFyfsaJruE99Kd6Eu/w5KLAGG+m0iqENCziDGzVA47TngKz2v
216 | PVA+aokoOyoz3b53qeti77ijatSEoKjxheBWpO+eoJeGq/e49Um3M2ogIX/JAlMa
217 | Inh+vYSYngQB2sx9LGkR9KHaMKNIGCDehk93Xla4pWJx1w==
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
233 |
235 |
237 |
239 |
240 |
241 |
242 | urn:oasis:names:tc:SAML:2.0:nameid-format:transient
243 | urn:mace:shibboleth:1.0:nameIdentifier
244 |
245 |
251 |
252 |
255 |
258 |
261 |
264 |
267 |
270 |
271 |
272 |
273 |
276 |
279 |
280 |
281 |
282 |
283 |
284 | TestShib Two Service Provider
285 | TestShib Two
286 | http://www.testshib.org/testshib-two/
287 |
288 |
289 | Nate
290 | Klingenstein
291 | ndk@internet2.edu
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
--------------------------------------------------------------------------------