Spring Security. Authentication and Authorization
Spring Security. Authentication and Authorization
SECURITY
AUTHENTICATION AND
AUTHORIZATION PROCESS
Mykola Demchyna
Agenda
Introduction
Authentication Process
Authorization Process
Authorization Process
• If you are using additional features like LDAP, OpenID, etc. you will
need to include additional dependencies.
Authentication vs. Authorization
@SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
public class Application implements CommandLineRunner {
spring.autoconfigure.exclude=org.springframework.boot.autoconf
igure
.security.SecurityAutoConfiguration
Minimal Java Configuration
• If we want a more flexible configuration, with multiple users and roles, the
first step is to create Spring Security configuration class that creates a
servlet Filter (known as the springSecurityFilterChain)
@Configuration
@EnableWebSecurity
public class WebSecurityConfigurer {
. . .
}
...
http.csrf().disable();
}
Form Login Java Configuration
@Configuration
• When we want to @EnableWebSecurity
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
provide their own login
page, we can customize @Override
protected void configure(HttpSecurity http) throws Exception {
WebSecurityConfigurer http.authorizeRequests()
Adapter and then .anyRequest().authenticated()
override the configure .and()
.formLogin()
method. .loginPage("/login-form")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/home")
.failureUrl("/login-form?error=true")
Log In process .permitAll()
.and()
.logout()
.logoutUrl("/perform-logout")
.logoutSuccessUrl("/login-form")
Log Out .deleteCookies("JSESSIONID")
process }
}
Form Login Java Configuration
<h2>LogIn Page</h2>
<div th:if="${param.error}">Invalid username and password.</div>
<div th:if="${param.logout}">You have been logged out.</div>
<form th:action="@{/login}" method="POST">
<label>Username:</label >
<input type="text" name="username" /><br>
<label >Password:</label > @Controller
<input type="password" name="password" /><br> public class LoginController {
<input type="submit" value="LogIn" /> @GetMapping("/login-form")
</form> public String login() {
return "login-page";
}
}
The HttpSecurity class
• The HttpSecurity class allows configuring web based security for specific http
requests.
Method Short description
Allows restricting access based upon the HttpServletRequest
authorizeRequests()
using
httpBasic() Configures HTTP Basic authentication.
formLogin() Specifies to support form based authentication.
logout() Provides logout support.
rememberMe() Allows configuring of Remember Me authentication.
anonymous() Allows configuring how an anonymous user is represented.
csrf() Adds CSRF support.
Allows configuring the HttpSecurity to only be invoked when
antMatcher(String antPattern)
matching the provided ant pattern.
Allows configuring the HttpSecurity to only be invoked when
regexMatcher(String pattern)
matching the provided regex pattern.
Allows configuring the HttpSecurity to only be invoked
mvcMatcher(String mvcPattern)
when matching the provided Spring MVC pattern.
The HttpSecurity class
Method Short description
addFilter(Filter filter) Adds a Filter that must be an instance of or extend
one of the Filters provided within the Security
framework.
addFilterBefore(Filter filter, Class Allows adding a Filter before one of the known Filter
beforeFilter) classes.
addFilterAfter(Filter filter, Class Allows adding a Filter after one of the known Filter
afterFilter) classes.
addFilterAt(Filter filter, Class atFilter) Adds the Filter at the location of the specified Filter
class.
exceptionHandling() Allows configuring exception handling.
authenticationProvider(AuthenticationProvider Allows adding an additional AuthenticationProvider to
provider) be used
userDetailsService(UserDetailsService service) Allows adding an additional UserDetailsService to be
used
sessionManagement() Allows configuring of Session Management.
cors() Adds a CorsFilter to be used.
headers() Adds the Security headers to the response.
Handling LogIn process
• The default is that accessing the URL "/logout" will log the user out by*:
• Invalidating the HTTP Session.
• Cleaning up any RememberMe authentication that was configured.
• Clearing the SecurityContextHolder.
• Redirect to "/login?logout" .
• However, you also have various options to further customize your logout
requirements.
* If CSRF protection is enabled (default), then the request must also be a POST.
Handling LogOut process
• To override standard LogOut process behavioral you can use methods from
LogoutConfigurer class.
Method Short description
The URL that triggers log out to occur (default is
logoutUrl(String url)
"/logout").
logoutSuccessUrl(String url) The URL to redirect to after logout has occurred.
logoutSuccessHandler(LogoutSuccessHandle
Sets the LogoutSuccessHandler to use.
r handler)
Configures SecurityContextLogoutHandler to invalidate
invalidateHttpSession(Boolean status)
the HttpSession at the time of logout.
Allows specifying the names of cookies to be removed
deleteCookies(String... cookieNames)
on logout success.
addLogoutHandler(LogoutHandler handler) Adds a LogoutHandler.
Specifies if SecurityContextLogoutHandler
clearAuthentication(Boolean status) should clear the Authentication at the time of
logout.
In-Memory Authentication
• Also Spring Security supports the use of an external properties file with
includes info about users.
• The properties file should contain entries in the form:
username=password,grantedAuthority[,grantedAuthority][,enabled|
disabled]
JDBC Authentication
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, UserDetailsService userDetailsService)
throws Exception {
auth.userDetailsService(userDetailsService);
}
UserDetails Interface
Implementation
public class User implements UserDetails {
private long id;
private String username;
private String password;
private Collection<? extends GrantedAuthority> roles;
@Override
public UserDetails loadUserByUsername(String username) {
if (user == null) {
throw new UsernameNotFoundException("User not found!");
}
return user;
}
}
Password Encoding
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (userDetails != null && passwordEncoder.matches(password, userDetails.getPassword())) {
return new UsernamePasswordAuthenticationToken(
userDetails.getUsername(),
userDetails.getPassword(),
userDetails.getAuthorities());
} else { return null; }
}
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, AuthenticationProvider provider)
throws Exception {
auth.authenticationProvider(provider);
}
Filter Chains
UsernamePasswordAuthenticationFilter
Process authentication, responds by default to “/login” URL.
AnonymousAuthenticationFilter
When there's no authentication object in SecurityContextHolder, it creates an anonymous
authentication object and put it there.
FilterSecurityInterceptor
Raise exceptions when access is denied.
ExceptionTranslationFilter
Catch any exceptions so that either an HTTP error response can be returned or an appropriate
AuthenticationEntryPoint can be launched.
Customizing Filter Chains
• The custom filters are configured inside configure(HttpSecurity http)
method of WebSecurityConfigurerAdapter.
• There are a couple of possible methods:
• addFilterBefore(filter, class) – adds a filter before the position of the specified
filter class.
• addFilterAfter(filter, class) – adds a filter after the position of the specified
filter class.
• addFilterAt(filter, class) – adds a filter at the location of the specified filter
class.
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET, "/login").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(webAuthenticationFilter,
UsernamePasswordAuthenticationFilter.class);
}
Customizing Filter Chains
• The UsernamePasswordAuthenticationFilter handles the authentication request
which extends from AbstractAuthenticationProcessingFilter and by default
responds to the URL “/login“.
@Component
public class WebAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@Autowired
public WebAuthenticationFilter(WebAuthenticationManager webAuthenticationManager) {
setAuthenticationManager(webAuthenticationManager);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
Authentication authentication = new UsernamePasswordAuthenticationToken(
request.getParameter("username"), request.getParameter("password")
);
return getAuthenticationManager().authenticate(authentication);
}
}
Customizing Filter Chains
• The UsernamePasswordAuthenticationFilter filter does the following
operations:
• First it checks for whether authentication is required or not based on our
HttpSecurity configuration. If authentication is not required, it simply invoke the
next filter in the chain.
• If authentication requires, then it calls the attemptAuthentication method and this
method can:
1. Return a populated authentication token for the
authenticated user, indicating successful authentication.
2. Return null, indicating that the authentication
process is still in progress.
3. Throw an AuthenticationException if the
authentication process fails.
Authentication Manager
@Autowired
private List<AuthenticationProvider> authenticationProviders;
@Override
public Authentication authenticate(Authentication authentication) {
Authentication webAuthentication;
for (AuthenticationProvider authenticationProvider : authenticationProviders) {
webAuthentication = authenticationProvider.authenticate(authentication);
if (webAuthentication != null) {
return webAuthentication;
}
}
throw new BadCredentialsException("Bad user Credentials!");
}
}
Authentication Success / Fails Handler
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, Authentication authResult) throws IOException, ServletException {
response.sendRedirect("/home");
}
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
response.sendRedirect("/login?error=true");
}
Handling Unauthorized Error
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(HttpMethod.GET,"/login").permitAll()
.anyRequest().authenticated();
http.exceptionHandling()
.authenticationEntryPoint(webAuthenticationEntryPoint);
}
Custom Authentication Entry Point
@Component
public class WebAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request,
HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
response.sendRedirect("/login");
}
}
Authorization
Process
Authorities
• All Authentication implementations store a list of GrantedAuthority objects.
• The GrantedAuthority objects are inserted into the Authentication object by
the AuthenticationManager and are later read by AccessDecisionManager s
when making authorization decisions.
• The GrantedAuthority is an interface with only one method:
String getAuthority();
• There are four annotations which support expression attributes to allow pre
and post-invocation authorization checks and also to support filtering of
submitted collection arguments or return values:
• @PreAuthorize – checks the given expression before entering the method.
• @PostAuthorize – verifies the given expression after execution of the
method
and could alter the result.
• @PreFilter – filters a collection argument before executing the method.
• @PostFilter – filters the returned collection after execution of the method.
Common Built-In Expressions
SpEL expression What it evaluates to
authentication The user’s authentication object obtained from the
SecurityContext
principal The user’s principal object
hasRole(role) true if the user has been granted the specified role
hasAnyRole(roles) true if the user has been granted any of the roles specified
hasAuthority(authority) true if the current principal has the specified authority
hasAnyAuthority(authorities) true if the current principal has any of the supplied authorities
• The main difference between Role and Authority is that, roles have special semantics
– starting with Spring Security 4, the 'ROLE_' prefix is automatically added by any
role related method.
equivale
hasAuthority('ROLE_ADMIN') nt hasRole('ADMIN')
Common Built-In Expressions
• Also, the @PostAuthorize annotation provides the ability to access the method
result using the built-in name returnObject in the expression.
@PostAuthorize("hasRole('EDITOR') or hasRole('VIEWER') and
returnObject.username == authentication.principal.username")
public User loadUser(String username) {
return userRoleRepository.loadUserByUserName(username);
}
Method Security Expressions
@PostFilter("filterObject != authentication.principal.username")
public List<String> loadAllUsernamesExceptCurrent() {
return userRoleRepository.getAllUsernames();
}
In this case, the name filterObject refers to the current object in the
returned collection.
Method Security Expressions
• You can also put security annotations at class level and use multiple
security annotations on one method :
@PreAuthorize("hasRole('EDITOR')")
public class UserService {
@PreAuthorize("#username == authentication.principal.username")
@PostAuthorize("returnObject.username == authentication.principal.username")
public User loadUser(String username) {
return userRoleRepository.loadUserByUserName(username);
}
}
Securing the View
• To securing the View use the Thymeleaf tag library:
xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
• The Spring Security’s JSP tag library is small and includes only three tags:
http.exceptionHandling()
.accessDeniedHandler(webAccessDeniedHandler);
}
Forbidden / Access Denied Handler
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException) throws IOException {
response.sendRedirect("/access-denied");
}
}