Approve or decline a request through the HandlerInterceptorAdapter

13

It is known that all requests by @RequestMapping are "public", where any user can access them through the browser.

The question is how to approve Requests where only the system can request and refuse such request if it is accessed by the user directly in the browser.

I'm currently validating a key from the Request Header , but there are plugins that can change the header, "shitting" with all logic.

For a better understanding, I'm using the following code to check the key , where the request has the key I'm comparing, it lets the request run, if not it blocks.

@Override
public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object controller) throws Exception {

    String Accept = request.getHeader("Authority");

    if(Accept != null && Accept.equalsIgnoreCase("KeyDefinida")){
        return true;    
    }
    return false;
}

Calling ajax

$.ajax
({
   url: "",
   headers: {          
     "Authority" : "KeyDefinida"
   },
    
asked by anonymous 15.04.2016 / 15:54

2 answers

4

Good morning, the most feasible, reliable and appropriate solution in your case I believe it would work with spring-security :

pom

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-config</artifactId>
</dependency>

To enable spring-secutiry you will need the @EnableWebSecurity You will need to configure the users into the spring context, you can set them inMemory if you only use the restriction to some entry points for the system, or fixed users:

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
    auth.inMemoryAuthentication()
        //Caso o usuario de sistema pode ser tbm adm, mais se preferir pode deixar apenas System
        .withUser("UsuarioSistema").password("SenhaUsuarioSistema").roles("SYSTEM", "ADMIN").and()
        //Não é obrigatório
        .withUser("usuario1").password("senha1").roles("USER");
}

Or work with an implementation of UserDetailsService

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService);
}

To work with UserDetailsService follow an example with jpa (but you can work with any database):

Your users table

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String username;
    private String password;
    @ManyToMany
    @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles;

    //Gets e Sets
}

Your Permissions table

@Entity
@Table(name = "role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
    @ManyToMany(mappedBy = "roles")
    private Set<User> users;

    //Gets e Sets
}

Repository Interface for the User

public interface UserRepository extends JpaRepository<User, Long> {
    User findByUsername(String username);
}

Repository interface for permissions

public interface RoleRepository extends JpaRepository<Role, Long>{
}

Deployment of UserDetailsService

@Service
public class UserDetailsServiceImpl implements UserDetailsService {
    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);

        Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
        for (Role role : user.getRoles()) {
            grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
        }

        return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(),
                grantedAuthorities);
    }
}

Finally the last step, you will need to configure the security standards that you will use, basically it allows you to make several configurations, I will leave an example configuration using x509 (private key and publish), protection against xss attack and disabling token of csrf actions (validation of actions allowed to the user). Spring-security has a universe of settings you can use or customize, suggestion of links spring docs , algaworks and devmedia

General configuration of spring-security

//Classe de configuração geral do spring-security
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    //Padrões de configuração de seu sistema
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //Autorização para requisições
        http.authorizeRequests()
            //Permite acesso de todos para / para frente
            .antMatchers("/**")
                .permitAll().and()
            //Permite acesso apenas de usuarios System para /meuEndPoint/ para frente
            .antMatcher("/meuEndPoint/**")
                .authorizeRequests()
                .anyRequest().hasRole("SYSTEM")
            .anyRequest().authenticated()
            .and().httpBasic();

        //Configurações
        http.x509().and()
            .headers().xssProtection().and().and()
            .csrf().disable();
    }

}

Still within the SecurityConfiguration settings, if you used UserDetailsService you will need to inject the UserDetailsService implementation and implement the configureGlobal method like this:

UserDetailsService Configuration

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
    }

If you have used the inMemory user you will need to implement configureGlobal this way:

InMemory Configuration

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        auth.inMemoryAuthentication()
            .withUser("UsuarioSistema").password("SenhaUsuarioSistema").roles("SYSTEM", "ADMIN").and()
            .withUser("usuario1").password("senha1").roles("USER");
    }

Another important point, if you want to use some type of encryption (highly recommended), you need to inject BCryptPasswordEncoder :

Encryption

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
    return new BCryptPasswordEncoder();
}

In addition to this change the configureGlobal to

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}

Remembering that with encryption enabled you should save in the bank or inMemory the password of users already encrypted.

    
29.03.2017 / 16:04
0

You can use the User-Agent header.

This header provides information where the user comes from.

Here is an example of User-Agent:

You can test with the browsers you want to support and only accept these headers.

I hope I have helped,

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object controller) throws Exception {

    String accept = request.getHeader("User-Agent"); //Deixei o nome da variável em CamelCase por padrão.

    if(accept != null && accept.contains("Mozilla")) {
        return true;    
    }
    return false;
}
    
25.05.2016 / 05:26