diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/jpabootstrap/application/Application.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/jpabootstrap/application/Application.java index f7b8e6bf6d..b547a60b06 100644 --- a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/jpabootstrap/application/Application.java +++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/jpabootstrap/application/Application.java @@ -8,7 +8,7 @@ public class Application { public static void main(String[] args) { EntityManager entityManager = getJpaEntityManager(); - User user = entityManager.find(User.class, 1); + User user = entityManager.find(User.class, 1l); System.out.println(user); entityManager.getTransaction().begin(); user.setName("John"); diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/onetoone/sharedkeybased/Address.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/onetoone/sharedkeybased/Address.java index 927516f6bb..e70c62e77b 100644 --- a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/onetoone/sharedkeybased/Address.java +++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/onetoone/sharedkeybased/Address.java @@ -1,9 +1,9 @@ package com.baeldung.hibernate.onetoone.sharedkeybased; - import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.JoinColumn; import javax.persistence.MapsId; import javax.persistence.OneToOne; import javax.persistence.Table; @@ -13,7 +13,7 @@ import javax.persistence.Table; public class Address { @Id - @Column(name = "id") + @Column(name = "user_id") private Long id; @Column(name = "street") @@ -24,6 +24,7 @@ public class Address { @OneToOne @MapsId + @JoinColumn(name = "user_id") private User user; public Long getId() { diff --git a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/onetoone/sharedkeybased/User.java b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/onetoone/sharedkeybased/User.java index fa00db1271..605671a149 100644 --- a/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/onetoone/sharedkeybased/User.java +++ b/persistence-modules/hibernate-jpa/src/main/java/com/baeldung/hibernate/onetoone/sharedkeybased/User.java @@ -8,6 +8,7 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.OneToOne; +import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; @Entity @@ -22,6 +23,7 @@ public class User { private String userName; @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) + @PrimaryKeyJoinColumn private Address address; public Long getId() { diff --git a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/EmailConfiguration.java b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/EmailConfiguration.java index 86a7f1162c..7f296ce6a7 100644 --- a/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/EmailConfiguration.java +++ b/spring-mvc-basics-2/src/main/java/com/baeldung/spring/configuration/EmailConfiguration.java @@ -119,7 +119,7 @@ public class EmailConfiguration { @Bean public ResourceBundleMessageSource emailMessageSource() { final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); - messageSource.setBasename("/mailMessages"); + messageSource.setBasename("mailMessages"); return messageSource; } diff --git a/spring-security-modules/pom.xml b/spring-security-modules/pom.xml index d5c0c0dd6e..0fc2b49fa7 100644 --- a/spring-security-modules/pom.xml +++ b/spring-security-modules/pom.xml @@ -28,6 +28,7 @@ spring-security-web-login spring-security-web-persisted-remember-me spring-security-web-sockets + spring-security-legacy-oidc spring-security-oidc spring-security-okta spring-security-web-react diff --git a/spring-security-modules/spring-security-legacy-oidc/README.md b/spring-security-modules/spring-security-legacy-oidc/README.md new file mode 100644 index 0000000000..9d47b35b21 --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/README.md @@ -0,0 +1,23 @@ +## Spring Security OpenID + +This module contains articles about OpenID with Spring Security + +### Relevant articles + +- [Spring Security and OpenID Connect (Legacy)](https://www.baeldung.com/spring-security-openid-connect-legacy) + +### OpenID Connect with Spring Security + +### Run the Project + +``` +mvn spring-boot:run +``` + +### Obtain Google App - Client ID, Secret + +- We need to get client id and client secret by creating a new project at [Google Developer Console](https://console.developers.google.com/project/_/apiui/credential?pli=1) +- We can follow these instructions to register our client application on their platform + +- Once we have the client id and secret, we have to make sure we add them to the application.properties file. + diff --git a/spring-security-modules/spring-security-legacy-oidc/pom.xml b/spring-security-modules/spring-security-legacy-oidc/pom.xml new file mode 100644 index 0000000000..a4ead0f6e0 --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/pom.xml @@ -0,0 +1,58 @@ + + + 4.0.0 + spring-security-legacy-oidc + spring-security-legacy-oidc + war + Spring OpenID Connect sample project + + + com.baeldung + parent-boot-2 + 0.0.1-SNAPSHOT + ../../parent-boot-2 + + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-tomcat + + + + org.springframework.security.oauth + spring-security-oauth2 + ${spring-security-oauth2.version} + + + + org.springframework.security + spring-security-jwt + ${spring-security-jwt.version} + + + + com.auth0 + jwks-rsa + ${jwks-rsa.version} + + + + + 2.2.1.RELEASE + 1.0.9.RELEASE + 0.3.0 + + + diff --git a/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/GoogleOpenIdConnectConfig.java b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/GoogleOpenIdConnectConfig.java new file mode 100644 index 0000000000..a9fdcfb286 --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/GoogleOpenIdConnectConfig.java @@ -0,0 +1,51 @@ +package com.baeldung.openid.oidc; + +import java.util.Arrays; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.client.OAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableOAuth2Client; + +@Configuration +@EnableOAuth2Client +public class GoogleOpenIdConnectConfig { + @Value("${google.clientId}") + private String clientId; + + @Value("${google.clientSecret}") + private String clientSecret; + + @Value("${google.accessTokenUri}") + private String accessTokenUri; + + @Value("${google.userAuthorizationUri}") + private String userAuthorizationUri; + + @Value("${google.redirectUri}") + private String redirectUri; + + @Bean + public OAuth2ProtectedResourceDetails googleOpenId() { + final AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails(); + details.setClientId(clientId); + details.setClientSecret(clientSecret); + details.setAccessTokenUri(accessTokenUri); + details.setUserAuthorizationUri(userAuthorizationUri); + details.setScope(Arrays.asList("openid", "email")); + details.setPreEstablishedRedirectUri(redirectUri); + details.setUseCurrentUri(false); + return details; + } + + @Bean + public OAuth2RestTemplate googleOpenIdTemplate(final OAuth2ClientContext clientContext) { + final OAuth2RestTemplate template = new OAuth2RestTemplate(googleOpenId(), clientContext); + return template; + } + +} \ No newline at end of file diff --git a/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/HomeController.java b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/HomeController.java new file mode 100644 index 0000000000..3d2e776eca --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/HomeController.java @@ -0,0 +1,22 @@ +package com.baeldung.openid.oidc; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class HomeController { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @RequestMapping("/") + @ResponseBody + public final String home() { + final String username = SecurityContextHolder.getContext().getAuthentication().getName(); + logger.info(username); + return "Welcome, " + username; + } + +} \ No newline at end of file diff --git a/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/OpenIdConnectFilter.java b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/OpenIdConnectFilter.java new file mode 100644 index 0000000000..c0b08bc548 --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/OpenIdConnectFilter.java @@ -0,0 +1,103 @@ +package com.baeldung.openid.oidc; + +import java.io.IOException; +import java.net.URL; +import java.security.interfaces.RSAPublicKey; +import java.util.Date; +import java.util.Map; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.jwt.Jwt; +import org.springframework.security.jwt.JwtHelper; +import org.springframework.security.jwt.crypto.sign.RsaVerifier; +import org.springframework.security.oauth2.client.OAuth2RestOperations; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; + +import com.auth0.jwk.Jwk; +import com.auth0.jwk.JwkProvider; +import com.auth0.jwk.UrlJwkProvider; +import com.fasterxml.jackson.databind.ObjectMapper; + +public class OpenIdConnectFilter extends AbstractAuthenticationProcessingFilter { + @Value("${google.clientId}") + private String clientId; + + @Value("${google.issuer}") + private String issuer; + + @Value("${google.jwkUrl}") + private String jwkUrl; + + public OAuth2RestOperations restTemplate; + + public OpenIdConnectFilter(String defaultFilterProcessesUrl) { + super(defaultFilterProcessesUrl); + setAuthenticationManager(new NoopAuthenticationManager()); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { + + OAuth2AccessToken accessToken; + try { + accessToken = restTemplate.getAccessToken(); + } catch (final OAuth2Exception e) { + throw new BadCredentialsException("Could not obtain access token", e); + } + try { + final String idToken = accessToken.getAdditionalInformation().get("id_token").toString(); + String kid = JwtHelper.headers(idToken) + .get("kid"); + final Jwt tokenDecoded = JwtHelper.decodeAndVerify(idToken, verifier(kid)); + final Map authInfo = new ObjectMapper().readValue(tokenDecoded.getClaims(), Map.class); + verifyClaims(authInfo); + final OpenIdConnectUserDetails user = new OpenIdConnectUserDetails(authInfo, accessToken); + return new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities()); + } catch (final Exception e) { + throw new BadCredentialsException("Could not obtain user details from token", e); + } + + } + + public void verifyClaims(Map claims) { + int exp = (int) claims.get("exp"); + Date expireDate = new Date(exp * 1000L); + Date now = new Date(); + if (expireDate.before(now) || !claims.get("iss").equals(issuer) || !claims.get("aud").equals(clientId)) { + throw new RuntimeException("Invalid claims"); + } + } + + + private RsaVerifier verifier(String kid) throws Exception { + JwkProvider provider = new UrlJwkProvider(new URL(jwkUrl)); + Jwk jwk = provider.get(kid); + return new RsaVerifier((RSAPublicKey) jwk.getPublicKey()); + } + + public void setRestTemplate(OAuth2RestTemplate restTemplate2) { + restTemplate = restTemplate2; + + } + + private static class NoopAuthenticationManager implements AuthenticationManager { + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + throw new UnsupportedOperationException("No authentication should be done with this AuthenticationManager"); + } + + } +} \ No newline at end of file diff --git a/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/OpenIdConnectUserDetails.java b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/OpenIdConnectUserDetails.java new file mode 100644 index 0000000000..4ff61bcad9 --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/OpenIdConnectUserDetails.java @@ -0,0 +1,81 @@ +package com.baeldung.openid.oidc; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.common.OAuth2AccessToken; + +public class OpenIdConnectUserDetails implements UserDetails { + + private static final long serialVersionUID = 1L; + + private String userId; + private String username; + private OAuth2AccessToken token; + + public OpenIdConnectUserDetails(Map userInfo, OAuth2AccessToken token) { + this.userId = userInfo.get("sub"); + this.username = userInfo.get("email"); + this.token = token; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public Collection getAuthorities() { + return Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")); + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public OAuth2AccessToken getToken() { + return token; + } + + public void setToken(OAuth2AccessToken token) { + this.token = token; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public String getPassword() { + return null; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + +} \ No newline at end of file diff --git a/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/SecurityConfig.java b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/SecurityConfig.java new file mode 100644 index 0000000000..fc5397a35b --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/SecurityConfig.java @@ -0,0 +1,48 @@ +package com.baeldung.openid.oidc; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.filter.OAuth2ClientContextFilter; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; +import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; + +@Configuration +@EnableWebSecurity +public class SecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + private OAuth2RestTemplate restTemplate; + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring().antMatchers("/resources/**"); + } + + @Bean + public OpenIdConnectFilter myFilter() { + final OpenIdConnectFilter filter = new OpenIdConnectFilter("/google-login"); + filter.setRestTemplate(restTemplate); + return filter; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .addFilterAfter(new OAuth2ClientContextFilter(), AbstractPreAuthenticatedProcessingFilter.class) + .addFilterAfter(myFilter(), OAuth2ClientContextFilter.class) + .httpBasic().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/google-login")) + .and() + .authorizeRequests() + // .antMatchers("/","/index*").permitAll() + .anyRequest().authenticated() + ; + + // @formatter:on + } +} \ No newline at end of file diff --git a/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/SpringOpenidApplication.java b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/SpringOpenidApplication.java new file mode 100644 index 0000000000..ec686f746d --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/src/main/java/com/baeldung/openid/oidc/SpringOpenidApplication.java @@ -0,0 +1,14 @@ +package com.baeldung.openid.oidc; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +@SpringBootApplication +public class SpringOpenidApplication extends SpringBootServletInitializer { + + public static void main(String[] args) { + SpringApplication.run(SpringOpenidApplication.class, args); + } + +} \ No newline at end of file diff --git a/spring-security-modules/spring-security-legacy-oidc/src/main/resources/application.properties b/spring-security-modules/spring-security-legacy-oidc/src/main/resources/application.properties new file mode 100644 index 0000000000..e9ae90dd10 --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/src/main/resources/application.properties @@ -0,0 +1,8 @@ +server.port=8081 +google.clientId=475873350264-g1opb20lf2fc60h0o84rrkn972krgkvo.apps.googleusercontent.com +google.clientSecret=GiA1Agf_aSt-bhTrnXjre-5Z +google.accessTokenUri=https://www.googleapis.com/oauth2/v3/token +google.userAuthorizationUri=https://accounts.google.com/o/oauth2/auth +google.redirectUri=http://localhost:8081/google-login +google.issuer=accounts.google.com +google.jwkUrl=https://www.googleapis.com/oauth2/v2/certs \ No newline at end of file diff --git a/spring-security-modules/spring-security-legacy-oidc/src/main/resources/logback.xml b/spring-security-modules/spring-security-legacy-oidc/src/main/resources/logback.xml new file mode 100644 index 0000000000..3b8d0b9964 --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/src/main/resources/logback.xml @@ -0,0 +1,13 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/spring-security-modules/spring-security-legacy-oidc/src/test/java/com/baeldung/openid/oidc/SpringContextTest.java b/spring-security-modules/spring-security-legacy-oidc/src/test/java/com/baeldung/openid/oidc/SpringContextTest.java new file mode 100644 index 0000000000..0a21c5e93f --- /dev/null +++ b/spring-security-modules/spring-security-legacy-oidc/src/test/java/com/baeldung/openid/oidc/SpringContextTest.java @@ -0,0 +1,15 @@ +package com.baeldung.openid.oidc; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = SpringOpenidApplication.class) +public class SpringContextTest { + + @Test + public void whenSpringContextIsBootstrapped_thenNoExceptions() { + } +} \ No newline at end of file diff --git a/spring-security-modules/spring-security-oidc/README.md b/spring-security-modules/spring-security-oidc/README.md index ca6053f70f..5e8f391b96 100644 --- a/spring-security-modules/spring-security-oidc/README.md +++ b/spring-security-modules/spring-security-oidc/README.md @@ -4,7 +4,6 @@ This module contains articles about OpenID with Spring Security ### Relevant articles -- [Spring Security and OpenID Connect](https://www.baeldung.com/spring-security-openid-connect) - [Spring Security and OpenID Connect (Legacy)](https://www.baeldung.com/spring-security-openid-connect-legacy) ### OpenID Connect with Spring Security diff --git a/spring-security-modules/spring-security-web-sockets/README.md b/spring-security-modules/spring-security-web-sockets/README.md index 14ef0c8b99..76717e2fe6 100644 --- a/spring-security-modules/spring-security-web-sockets/README.md +++ b/spring-security-modules/spring-security-web-sockets/README.md @@ -5,7 +5,7 @@ This module contains articles about WebSockets with Spring Security ### Relevant Articles: - [Intro to Security and WebSockets](https://www.baeldung.com/spring-security-websockets) -- [Spring WebSockets: Build an User Chat](https://www.baeldung.com/spring-websockets-send-message-to-user) +- [Spring WebSockets: Send Messages to a Specific User](https://www.baeldung.com/spring-websockets-send-message-to-user) - [REST vs WebSockets](https://www.baeldung.com/rest-vs-websockets) ### Running This Project: