Detail planning evening 4
-
Security: 2 points
-
Mapping in JPA: 2 points
-
@OneToOne
-
@OneToMany
-
@ManyToMany
-
-
Some useful TipsnTricks
-
Spring Actuator
-
Items mentioned below
-
-
Q&A, recap
Security
-
Show implementing a very simple security
-
Show more complex
Simple Security - for starters
Add this to pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Add SecurityConfig.java in config package
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
@Configuration
@EnableGlobalMethodSecurity
@EnableWebSecurity
// see
// http://docs.spring.io/spring-security/site/docs/3.2.x/reference/htmlsingle/#multiple-httpsecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
public static final String APIROLE = "APIROLE";
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth,
@Value("${api.username}") String apiUsername,
@Value("${api.password}") String apiPassword) throws Exception{
auth.inMemoryAuthentication().withUser(apiUsername).password(apiPassword).roles(APIROLE);
}
@Configuration
@Order(1)
public static class AuWebSecurityAdapterRest extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests().antMatchers("/api/**").hasRole(APIROLE).and().httpBasic();
http.authorizeRequests().anyRequest().permitAll();
// the rest is implicit denied
}
}
}
Add this to the (appropriate) application.properties
server.port=8081
api.username=<ausername>
api.password=<apassword>
Advanced Security using an authentication provider
Add a domain class for the User
Add
public class User implements Serializable {
private String username;
private String password;
// add set/get
}
Create a repo for fetching DB stuff
@Component
public interface UserRepository extends CrudRepository<User, Long>{
User findByUsernameAndPassword(String username, String password);
}
Create a UserService
Create this
@Service
@Transactional
public class UserService {
private static final Logger LOGGER = LoggerFactory.getLogger(UserService.class);
@Autowired
private UserRepository repository;
public void save(User user) {
this.repository.save(user);
}
public boolean authenticate(String username, String password) {
return this.repository.findByUsernameAndPassword(username, password) != null;
}
Create a UserAuthenticationProvider
@Component
public class UserAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(UserAuthenticationProvider.class);
@Autowired
private UserService userService;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final String username = authentication.getName();
final String password = authentication.getCredentials().toString();
if (this.userService.authenticate(username, password)) {
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new AuthenticationTokenGrantedAuthority());
LOGGER.debug("Validation succeeded for [{}]!", username);
return new UsernamePasswordAuthenticationToken(username, password, grantedAuths);
}
else {
LOGGER.error("Validation failed for [{}]", username);
throw new AuthenticationTokenAuthenticationException(String.format("Validation failed for %s", username));
}
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
class AuthenticationTokenAuthenticationException extends AuthenticationException {
private static final long serialVersionUID = -9139561336028106640L;
public AuthenticationTokenAuthenticationException(String msg) {
super(msg);
}
}
class AuthenticationTokenGrantedAuthority implements GrantedAuthority {
private static final long serialVersionUID = -4893914488018936401L;
@Override
public String getAuthority() {
return "ROLE_"+SecurityConfig.BIRDS;
}
}
Finally wire the UserAuthenticationProvider in the SecurityConfig
Do that yourself
-
Using the things you learned during this course
Solution
In SecurityConfig add
@Autowired
private UserAuthenticationProvider authenticationProvider;
public static final String BIRDS = "BIRDS"; // or whatever you like :-)
In configureGlobal add
auth.authenticationProvider(authenticationProvider);
Mapping in JPA
You can now remove the other @Value based security for testing
-
Show an other detail presentation
Running the Spring Actuator
Add the following to POM.XML
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator-docs</artifactId>
</dependency>
Add the following to application.properties
management.context-path=/actuator
management.security.enabled=false
Security is enabled by default which is OK of course You should have role ACTUATOR to be able to open the actuator endpoints
Invoking the Actuator using Postman
-
Open a REST client (Postman)
-
Open url http://localhost:<port>/actuator/