Skip to content

Commit 1ed3633

Browse files
committed
Code updates
1 parent df54ae4 commit 1ed3633

File tree

19 files changed

+953
-10
lines changed

19 files changed

+953
-10
lines changed

spring-security/getting-started/SecureApplication/pom.xml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,37 @@
4040
<groupId>org.springframework.boot</groupId>
4141
<artifactId>spring-boot-starter-thymeleaf</artifactId>
4242
</dependency>
43+
<dependency>
44+
<groupId>org.projectlombok</groupId>
45+
<artifactId>lombok</artifactId>
46+
<version>1.18.20</version>
47+
<scope>provided</scope>
48+
</dependency>
49+
<dependency>
50+
<groupId>org.projectlombok</groupId>
51+
<artifactId>lombok-mapstruct-binding</artifactId>
52+
<version>0.2.0</version>
53+
</dependency>
54+
<dependency>
55+
<groupId>org.springframework.boot</groupId>
56+
<artifactId>spring-boot-starter-data-jpa</artifactId>
57+
</dependency>
58+
<dependency>
59+
<groupId>org.mapstruct</groupId>
60+
<artifactId>mapstruct</artifactId>
61+
<version>1.4.2.Final</version>
62+
</dependency>
63+
<dependency>
64+
<groupId>org.hsqldb</groupId>
65+
<artifactId>hsqldb</artifactId>
66+
<version>2.4.0</version>
67+
<scope>runtime</scope>
68+
</dependency>
69+
<dependency>
70+
<groupId>org.zalando</groupId>
71+
<artifactId>problem-spring-web</artifactId>
72+
<version>0.27.0</version>
73+
</dependency>
4374
</dependencies>
4475

4576
<build>
@@ -48,6 +79,27 @@
4879
<groupId>org.springframework.boot</groupId>
4980
<artifactId>spring-boot-maven-plugin</artifactId>
5081
</plugin>
82+
<plugin>
83+
<groupId>org.apache.maven.plugins</groupId>
84+
<artifactId>maven-compiler-plugin</artifactId>
85+
<version>3.8.0</version>
86+
<configuration>
87+
<source>11</source>
88+
<target>11</target>
89+
<annotationProcessorPaths>
90+
<path>
91+
<groupId>org.projectlombok</groupId>
92+
<artifactId>lombok</artifactId>
93+
<version>1.18.20</version>
94+
</path>
95+
<path>
96+
<groupId>org.mapstruct</groupId>
97+
<artifactId>mapstruct-processor</artifactId>
98+
<version>1.4.2.Final</version>
99+
</path>
100+
</annotationProcessorPaths>
101+
</configuration>
102+
</plugin>
51103
</plugins>
52104
</build>
53105

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.reflectoring.security.config;
2+
3+
import org.springframework.boot.context.properties.ConfigurationProperties;
4+
import org.springframework.security.core.userdetails.User;
5+
import org.springframework.security.core.userdetails.UserDetails;
6+
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
7+
8+
import java.util.Map;
9+
import java.util.Set;
10+
import java.util.stream.Collectors;
11+
12+
@ConfigurationProperties(prefix = "auth")
13+
public class BasicAuthProperties {
14+
15+
private Map<String, UserDetail> users;
16+
17+
public Map<String, UserDetail> getUsers() {
18+
return users;
19+
}
20+
21+
public void setUsers(Map<String, UserDetail> users) {
22+
this.users = users;
23+
}
24+
25+
public Set<UserDetails> getUserDetails() {
26+
return this.users.entrySet().stream()
27+
.map(entry -> User.withUsername(entry.getKey())
28+
.passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
29+
.password(entry.getValue().getPassword())
30+
.roles(entry.getValue().getRole().toUpperCase())
31+
.build())
32+
.collect(Collectors.toSet());
33+
}
34+
35+
private static class UserDetail {
36+
37+
private String password;
38+
39+
private String role;
40+
41+
public String getPassword() {
42+
return password;
43+
}
44+
45+
public void setPassword(String password) {
46+
this.password = password;
47+
}
48+
49+
public String getRole() {
50+
return role;
51+
}
52+
53+
public void setRole(String role) {
54+
this.role = role;
55+
}
56+
}
57+
58+
}

spring-security/getting-started/SecureApplication/src/main/java/com/reflectoring/security/config/SecurityConfiguration.java

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,87 @@
11
package com.reflectoring.security.config;
22

3+
import com.reflectoring.security.exception.UserAuthenticationErrorHandler;
4+
import com.reflectoring.security.exception.UserForbiddenErrorHandler;
5+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
36
import org.springframework.context.annotation.Bean;
47
import org.springframework.context.annotation.Configuration;
5-
import org.springframework.security.config.Customizer;
8+
import org.springframework.core.annotation.Order;
9+
import org.springframework.http.HttpMethod;
610
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
711
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
12+
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
813
import org.springframework.security.config.http.SessionCreationPolicy;
14+
import org.springframework.security.core.userdetails.UserDetailsService;
15+
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
16+
import org.springframework.security.web.AuthenticationEntryPoint;
917
import org.springframework.security.web.SecurityFilterChain;
10-
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
1118

1219
@Configuration
1320
@EnableWebSecurity
21+
@EnableConfigurationProperties(BasicAuthProperties.class)
1422
public class SecurityConfiguration {
1523

24+
private final BasicAuthProperties props;
25+
26+
public SecurityConfiguration(BasicAuthProperties props) {
27+
this.props = props;
28+
}
29+
30+
@Bean
31+
@Order(1)
32+
public SecurityFilterChain bookFilterChain(HttpSecurity http) throws Exception {
33+
http
34+
.csrf().disable()
35+
.sessionManagement(session -> session
36+
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
37+
.antMatcher("/library/**")
38+
.authorizeRequests()
39+
.antMatchers(HttpMethod.GET, "/library/**").hasRole("USER").anyRequest().authenticated()
40+
.and()
41+
.httpBasic()
42+
.and()
43+
.exceptionHandling(exception -> exception
44+
.authenticationEntryPoint(userAuthenticationErrorHandler())
45+
.accessDeniedHandler(new UserForbiddenErrorHandler()));
46+
47+
return http.build();
48+
}
49+
50+
@Bean
51+
public WebSecurityCustomizer webSecurityCustomizer() {
52+
return (web) -> web.ignoring().antMatchers("/library/info");
53+
}
54+
55+
@Bean
56+
public UserDetailsService userDetailsService() {
57+
return new InMemoryUserDetailsManager(props.getUserDetails());
58+
}
59+
60+
/*@Bean
61+
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
62+
63+
var builder = http.getSharedObject(AuthenticationManagerBuilder.class)
64+
.userDetailsService(new InMemoryUserDetailsManager(props.getUserDetails()));
65+
return builder.and().build();
66+
}*/
67+
68+
@Bean
69+
public AuthenticationEntryPoint userAuthenticationErrorHandler() {
70+
UserAuthenticationErrorHandler userAuthenticationErrorHandler =
71+
new UserAuthenticationErrorHandler();
72+
userAuthenticationErrorHandler.setRealmName("Basic Authentication");
73+
return userAuthenticationErrorHandler;
74+
}
75+
76+
/*@Bean
77+
public UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter(HttpSecurity http) throws Exception {
78+
79+
//AuthenticationEntryPoint authenticationEntryPoint = new UserAuthenticationErrorHandler();
80+
return new UsernamePasswordAuthenticationFilter(authenticationManager(http));
81+
}*/
82+
1683
public static final String[] ENDPOINTS_WHITELIST = {
1784
"/css/**",
18-
"/",
1985
"/login",
2086
"/home"
2187
};
@@ -26,12 +92,14 @@ public class SecurityConfiguration {
2692
public static final String PASSWORD = "password";
2793

2894
@Bean
95+
@Order(1)
2996
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
3097
// Requests
3198
http.authorizeRequests(request -> request.antMatchers(ENDPOINTS_WHITELIST).permitAll()
3299
.anyRequest().authenticated())
33100
// CSRF
34101
.csrf().disable()
102+
.antMatcher("/login")
35103
//.formLogin(Customizer.withDefaults())
36104
.formLogin(form -> form
37105
.loginPage(LOGIN_URL)
@@ -53,8 +121,54 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
53121
.maximumSessions(1)
54122
.maxSessionsPreventsLogin(true));
55123

56-
57124
return http.build();
58125
}
126+
127+
/*private AuthenticationFilter authenticationFilter(HttpSecurity http) {
128+
AuthenticationFilter filter = new AuthenticationFilter(
129+
resolver(http), authenticationConverter());
130+
filter.setSuccessHandler((request, response, auth) -> {});
131+
return filter;
132+
}
133+
134+
public AuthenticationConverter authenticationConverter() {
135+
return new BasicAuthenticationConverter();
136+
}
137+
138+
public AuthenticationManagerResolver<HttpServletRequest> resolver(HttpSecurity http) {
139+
return request -> {
140+
if (request.getPathInfo().contains("login")) {
141+
try {
142+
return customAuthenticationManager(http);
143+
} catch (Exception e) {
144+
e.printStackTrace();
145+
}
146+
}
147+
return null;
148+
};
149+
}
150+
151+
public AuthenticationManager customAuthenticationManager(HttpSecurity http)
152+
throws Exception {
153+
return http.getSharedObject(AuthenticationManagerBuilder.class)
154+
.userDetailsService(userDetailsService())
155+
.passwordEncoder(passwordEncoder())
156+
.and()
157+
.build();
158+
}
159+
160+
public InMemoryUserDetailsManager userDetailsService() {
161+
UserDetails admin = User.withUsername("user")
162+
.password(passwordEncoder().encode("userpass"))
163+
.roles("USER")
164+
.build();
165+
return new InMemoryUserDetailsManager(admin);
166+
}
167+
168+
public PasswordEncoder passwordEncoder() {
169+
return new BCryptPasswordEncoder();
170+
}*/
171+
172+
59173
}
60174

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.reflectoring.security.exception;
2+
3+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
4+
import com.fasterxml.jackson.annotation.JsonInclude;
5+
import org.zalando.problem.AbstractThrowableProblem;
6+
import org.zalando.problem.StatusType;
7+
8+
import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY;
9+
import static org.zalando.problem.Status.FORBIDDEN;
10+
import static org.zalando.problem.Status.UNAUTHORIZED;
11+
12+
@JsonInclude(NON_EMPTY)
13+
@JsonIgnoreProperties({"stackTrace", "type", "title", "message", "localizedMessage", "parameters"})
14+
public class CommonException extends AbstractThrowableProblem {
15+
16+
private CommonException(StatusType status, String detail) {
17+
super(null, null, status, detail, null, null, null);
18+
}
19+
20+
public static CommonException unauthorized() {
21+
return new CommonException(UNAUTHORIZED, "Unauthorised or Bad Credentials");
22+
}
23+
24+
public static CommonException forbidden() {
25+
return new CommonException(FORBIDDEN, "Forbidden");
26+
}
27+
28+
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.reflectoring.security.exception;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import org.springframework.security.core.AuthenticationException;
5+
import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
6+
7+
import javax.servlet.http.HttpServletRequest;
8+
import javax.servlet.http.HttpServletResponse;
9+
import java.io.IOException;
10+
import java.io.PrintWriter;
11+
12+
public class UserAuthenticationErrorHandler extends BasicAuthenticationEntryPoint {
13+
private final ObjectMapper objectMapper;
14+
15+
public UserAuthenticationErrorHandler() {
16+
this.objectMapper = new ObjectMapper();
17+
}
18+
19+
@Override
20+
public void commence(HttpServletRequest request,
21+
HttpServletResponse response,
22+
AuthenticationException ex) throws IOException {
23+
/*response.setContentType("application/json");
24+
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
25+
response.getOutputStream().println(objectMapper.writeValueAsString(CommonException.unauthorized()));
26+
*/
27+
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
28+
response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName() + "");
29+
30+
final PrintWriter writer = response.getWriter();
31+
writer.println("HTTP Status 401 : " + ex.getMessage());
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.reflectoring.security.exception;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import org.springframework.security.access.AccessDeniedException;
5+
import org.springframework.security.web.access.AccessDeniedHandler;
6+
7+
import javax.servlet.http.HttpServletRequest;
8+
import javax.servlet.http.HttpServletResponse;
9+
import java.io.IOException;
10+
11+
public class UserForbiddenErrorHandler implements AccessDeniedHandler {
12+
13+
private final ObjectMapper objectMapper;
14+
15+
public UserForbiddenErrorHandler()
16+
{
17+
this.objectMapper = new ObjectMapper();
18+
}
19+
20+
@Override
21+
public void handle(HttpServletRequest request,
22+
HttpServletResponse response,
23+
AccessDeniedException ex) throws IOException {
24+
response.setContentType("application/json");
25+
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
26+
response.getOutputStream().println(objectMapper.writeValueAsString(CommonException.forbidden()));
27+
}
28+
}

0 commit comments

Comments
 (0)