├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
└── main
├── java
└── com
│ └── hellokoding
│ └── sso
│ └── resource
│ ├── CookieUtil.java
│ ├── JwtFilter.java
│ ├── JwtUtil.java
│ ├── ResourceController.java
│ └── WebApplication.java
├── resources
└── application.properties
└── webapp
└── protected-resource.ftl
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | out
3 | .settings
4 | .classpath
5 | .project
6 | .idea
7 | *.iml
8 | *.DS_Store
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Hello Koding
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Single Sign On (SSO) Example with JSON Web Token (JWT), Spring Boot - Resource Service
2 |
3 | ## Guide
4 | https://hellokoding.com/hello-single-sign-on-sso-with-json-web-token-jwt-spring-boot/
5 |
6 | ## What you'll need
7 | - JDK 1.7+
8 | - Maven 3+
9 |
10 | ## Stack
11 | - Java
12 | - Spring Boot
13 | - FreeMarker
14 |
15 | ## Run
16 | - Run [Authentication Service](https://github.com/hellokoding/hello-sso-jwt-auth): `mvn spring-boot:run`
17 | - Run Resource Service 1: `mvn spring-boot:run -Dserver.port=8180`
18 | - Run Resource Service 2: `mvn spring-boot:run -Dserver.port=8280`
19 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 | hello-sso-jwt-resource
5 | hello-sso-jwt-resource
6 | hello-sso-jwt-resource
7 |
8 | org.springframework.boot
9 | spring-boot-starter-parent
10 | 1.3.5.RELEASE
11 |
12 |
13 |
14 | 1.7
15 |
16 |
17 |
18 |
19 | org.springframework.boot
20 | spring-boot-starter-freemarker
21 |
22 |
23 | io.jsonwebtoken
24 | jjwt
25 | 0.6.0
26 |
27 |
28 |
29 |
30 |
31 | org.springframework.boot
32 | spring-boot-maven-plugin
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/main/java/com/hellokoding/sso/resource/CookieUtil.java:
--------------------------------------------------------------------------------
1 | package com.hellokoding.sso.resource;
2 |
3 | import org.springframework.web.util.WebUtils;
4 | import javax.servlet.http.Cookie;
5 | import javax.servlet.http.HttpServletRequest;
6 | import javax.servlet.http.HttpServletResponse;
7 |
8 | public class CookieUtil {
9 | public static void create(HttpServletResponse httpServletResponse, String name, String value, Boolean secure, Integer maxAge, String domain) {
10 | Cookie cookie = new Cookie(name, value);
11 | cookie.setSecure(secure);
12 | cookie.setHttpOnly(true);
13 | cookie.setMaxAge(maxAge);
14 | cookie.setDomain(domain);
15 | cookie.setPath("/");
16 | httpServletResponse.addCookie(cookie);
17 | }
18 |
19 | public static void clear(HttpServletResponse httpServletResponse, String name) {
20 | Cookie cookie = new Cookie(name, null);
21 | cookie.setPath("/");
22 | cookie.setHttpOnly(true);
23 | cookie.setMaxAge(0);
24 | cookie.setDomain("localhost");
25 | httpServletResponse.addCookie(cookie);
26 | }
27 |
28 | public static String getValue(HttpServletRequest httpServletRequest, String name) {
29 | Cookie cookie = WebUtils.getCookie(httpServletRequest, name);
30 | return cookie != null ? cookie.getValue() : null;
31 | }
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/src/main/java/com/hellokoding/sso/resource/JwtFilter.java:
--------------------------------------------------------------------------------
1 | package com.hellokoding.sso.resource;
2 |
3 | import org.springframework.stereotype.Component;
4 | import org.springframework.web.filter.OncePerRequestFilter;
5 |
6 | import javax.servlet.FilterChain;
7 | import javax.servlet.ServletException;
8 | import javax.servlet.http.HttpServletRequest;
9 | import javax.servlet.http.HttpServletResponse;
10 | import java.io.IOException;
11 |
12 | @Component
13 | public class JwtFilter extends OncePerRequestFilter {
14 | private static final String jwtTokenCookieName = "JWT-TOKEN";
15 | private static final String signingKey = "signingKey";
16 |
17 | @Override
18 | protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
19 | String username = JwtUtil.getSubject(httpServletRequest, jwtTokenCookieName, signingKey);
20 | if(username == null){
21 | String authService = this.getFilterConfig().getInitParameter("services.auth");
22 | httpServletResponse.sendRedirect(authService + "?redirect=" + httpServletRequest.getRequestURL());
23 | } else{
24 | httpServletRequest.setAttribute("username", username);
25 | filterChain.doFilter(httpServletRequest, httpServletResponse);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/hellokoding/sso/resource/JwtUtil.java:
--------------------------------------------------------------------------------
1 | package com.hellokoding.sso.resource;
2 |
3 | import io.jsonwebtoken.JwtBuilder;
4 | import io.jsonwebtoken.Jwts;
5 | import io.jsonwebtoken.SignatureAlgorithm;
6 |
7 | import javax.servlet.http.HttpServletRequest;
8 | import java.util.Date;
9 |
10 | public class JwtUtil {
11 | public static String generateToken(String signingKey, String subject) {
12 | long nowMillis = System.currentTimeMillis();
13 | Date now = new Date(nowMillis);
14 |
15 | JwtBuilder builder = Jwts.builder()
16 | .setSubject(subject)
17 | .setIssuedAt(now)
18 | .signWith(SignatureAlgorithm.HS256, signingKey);
19 |
20 | return builder.compact();
21 | }
22 |
23 | public static String getSubject(HttpServletRequest httpServletRequest, String jwtTokenCookieName, String signingKey){
24 | String token = CookieUtil.getValue(httpServletRequest, jwtTokenCookieName);
25 | if(token == null) return null;
26 | return Jwts.parser().setSigningKey(signingKey).parseClaimsJws(token).getBody().getSubject();
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/src/main/java/com/hellokoding/sso/resource/ResourceController.java:
--------------------------------------------------------------------------------
1 | package com.hellokoding.sso.resource;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 |
6 | import javax.servlet.http.HttpServletResponse;
7 |
8 | @Controller
9 | public class ResourceController {
10 | private static final String jwtTokenCookieName = "JWT-TOKEN";
11 |
12 | @RequestMapping("/")
13 | public String home() {
14 | return "redirect:/protected-resource";
15 | }
16 |
17 | @RequestMapping("/protected-resource")
18 | public String protectedResource() {
19 | return "protected-resource";
20 | }
21 |
22 | @RequestMapping("/logout")
23 | public String logout(HttpServletResponse httpServletResponse) {
24 | CookieUtil.clear(httpServletResponse, jwtTokenCookieName);
25 | return "redirect:/";
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/com/hellokoding/sso/resource/WebApplication.java:
--------------------------------------------------------------------------------
1 | package com.hellokoding.sso.resource;
2 |
3 | import org.springframework.beans.factory.annotation.Value;
4 | import org.springframework.boot.SpringApplication;
5 | import org.springframework.boot.autoconfigure.SpringBootApplication;
6 | import org.springframework.boot.builder.SpringApplicationBuilder;
7 | import org.springframework.boot.context.embedded.FilterRegistrationBean;
8 | import org.springframework.boot.context.web.SpringBootServletInitializer;
9 | import org.springframework.context.annotation.Bean;
10 |
11 | import java.util.Collections;
12 |
13 | @SpringBootApplication
14 | public class WebApplication extends SpringBootServletInitializer {
15 | @Value("${services.auth}")
16 | private String authService;
17 |
18 | @Bean
19 | public FilterRegistrationBean jwtFilter() {
20 | final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
21 | registrationBean.setFilter(new JwtFilter());
22 | registrationBean.setInitParameters(Collections.singletonMap("services.auth", authService));
23 | registrationBean.addUrlPatterns("/protected-resource");
24 |
25 | return registrationBean;
26 | }
27 |
28 | @Override
29 | protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
30 | return application.sources(WebApplication.class);
31 | }
32 |
33 | public static void main(String[] args) throws Exception {
34 | SpringApplication.run(WebApplication.class, args);
35 | }
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.freemarker.template-loader-path: /
2 | spring.freemarker.suffix: .ftl
3 |
4 | services.auth=http://localhost:8080/login
--------------------------------------------------------------------------------
/src/main/webapp/protected-resource.ftl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Protected Resource Service
5 |
6 |
7 | Hello, ${Request.username!}
8 | Logout
9 |
10 |
--------------------------------------------------------------------------------