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