Aprovar ou recusar uma requisição através do HandlerInterceptorAdapter – java segurança spring-mvc

Pergunta:


Sabe-se que todas as requisições por @RequestMapping são “públicas”, onde qualquer usuário pode acessá-las através do navegador.

A questão é, como aprovar Requests onde apenas o sistema pode requisitar e recusar tal request caso sejá acessado pelo usuário diretamente no navegador.

Atualmente estou validando uma key vinda do Request Header, porém existem plugins que podem alterar o header, “cagando” com toda a lógica.

Para melhor entendimento, estou usando o seguinte codigo para verificar a key, onde caso o request tenha a key que estou comparando, ele deixa o request ser executado, se não ele bloqueia.

@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;
}

Chamada ajax

$.ajax
({
   url: "",
   headers: {          
     "Authority" : "KeyDefinida"
   },

Autor da pergunta Matheus

brow-joe

Bom dia, a solução mais viável, confiável e adequada no seu caso acredito que seria trabalhar com o 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>

Para habilitar o spring-secutiry você vai precisar da anotação @EnableWebSecurity
Você vai precisar configurar os usuários para dentro do contexto spring, você pode configurá-los inMemory se for utilizar apenas a restrição a alguns entry points para o sistema, ou usuários fixos:

@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");
}

Ou trabalhar com uma implementação de UserDetailsService

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

Para trabalhar com o UserDetailsService segue um exemplo com jpa (mas você pode trabalhar com qualquer base de dados):

Sua tabela de usuários

@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
}

Sua tabela de Permissões

@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
}

Interface de repositório para o usuario

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

Interface de repositório para as permissões

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

Implementação da 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);
    }
}

Finalmente o ultimo passo, você vai precisar configurar os padrões de segurança que irá utilizar, basicamente ele lhe permite fazer várias configurações, vou deixar um exemplo de configuração utilizando x509 (chave privada e publica), proteção contra ataque xss e desabilitando o token de ações do csrf (validação de ações permitidas ao usuário). O spring-security existe um universo de configurações que você pode utilizar ou personalizar, sugestão de links spring docs, algaworks e devmedia

Configuração geral do 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();
    }

}

Ainda dentro das configurações SecurityConfiguration, se você utilizou o UserDetailsService vai precisar injetar a implementação de UserDetailsService e implementar o método configureGlobal desta forma:

Configuração UserDetailsService

    @Autowired
    private UserDetailsService userDetailsService;

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

caso tenha utilizado o usuario inMemory vc vai precisar implementar o configureGlobal desta forma:

Configuração inMemory

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

Outro ponto importante, se você deseja utilizar algum tipo de criptografia (altamente recomendado), precisa injetar o BCryptPasswordEncoder:

Criptografia

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

além disto mudar o configureGlobal para

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

lembrando que com a criptografia habilitada você deve salvar no banco ou inMemory a senha de usuários já encriptada.

Você pode utilizar o header “User-Agent”.

Este header oferece informações de onde o usuário vem.

Segue um exemplo de User-Agent:

User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36

Você pode testar com os navegadores que deseja dar suporte e apenas aceitar estes headers.

Espero ter ajudado,

@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;
}

Fonte

Related Posts:

Qual a diferença entre AppCompatActivity e Activity? – android android-activity
Pergunta: Qual a diferença da AppCompatActivity para Activity ? A partir de qual versão a AppCompatActivity foi adicionada ao Android? Autor da pergunta Luhhh A diferença reside ...
Como abreviar palavras em PHP? – php string
Pergunta: Possuo informações comuns como nome de pessoas e endereços, e preciso que elas contenham no máximo 30 caracteres sem cortar palavras. Exemplo: 'Avenida Natalino João Brescansin' ...
Qual é a finalidade de um parêntese vazio numa declaração Lambda? – c# expressões-lambda característica-linguagem
Pergunta: Criei um exemplo de uma declaração Lambda sem argumentos, entretanto, estou com duvidas referente a omissão do parêntese vazio () na declaração. Veja o exemplo: class ...
Boas práticas para URI em API RESTful – api rest restful
Pergunta: Estou com dúvida em relação às URIs de alguns recursos da api que estou desenvolvendo. Tenho os recursos projetos e atividades com relação 1-N, ...
Dúvidas sobre a integração do MySQL com Java – java mysql netbeans
Pergunta: Estou criando um sistema no NetBeans, utilizando a linguagem Java e o banco de dados MySQL. Escrevi o seguinte código para realizar a conexão ...
Qual é a finalidade da pasta Model do framework Inphinit? – php inphinit
Pergunta: No Inphinit micro-framework existe a pasta Model que fica dentro da pasta application, e nela é onde ficam as classes, mas eu estou muito ...
Uso do ‘@’ em variáveis – javascript typescript coffeescript
Pergunta: Vejo em algumas linguagens que compilam para javascript, como TypeScript e CoffeeScript, o uso do @ em variáveis, como também, casos em que o ...
Qual tamanho máximo um arquivo JSON pode ter? – json arquivo
Pergunta: Vou dar um exemplo para conseguir explicar minha duvida: Preciso recuperar informação de imagens vindas de uma API, esse banco de imagens me retorna JSON's ...
O que é Teste de Regressão? – terminologia engenharia-de-software testes
Pergunta: Na matéria de Teste de Software o professor abordou um termo chamado Teste de Regressão, isto dentro da disciplina de teste de software. Sendo ...
O que é um construtor da linguagem? – php característica-linguagem
Pergunta: Em PHP, já li e ouvi várias vezes a respeito dos Construtores da Linguagem. Os casos que sempre ouvi falar deles foi em casos ...
Função intrínseca para converter numérico para string – cobol
Pergunta: Estou a tentar saber se existe alguma função intrínseca do COBOL para converter um data numérico para string sem precisar usar a cláusula REDEFINES: ( ...
Porque usar implements? – java android
Pergunta: Qual a diferença entre usar btn.setOnClickListener(new OnClickListener() { e public class MainActivity extends Activity implements OnClickListener{ Estive fazendo um curso de Android e meu professor falou que ...
O que é XHTML e quando deve ser usado? – html xml xhtml
Pergunta: O que eu sei é que o XHTML precisa ser XML válido. Isso implica, por exemplo, que todas as tags precisam ser fechadas. Por ...
Uma placa aceleradora de vídeo pode melhorar o desempenho não-gráfico? [fechada] – desempenho
Pergunta: Para desenvolver em Ruby on Rails, eu utilizo aqui uma máquina virtual do VirtualBox com Ubuntu Server 14.04 sem interface gráfica instalada. Recentemente descobri uma ...
Concat() VS Union() – c# .net
Pergunta: Qual a diferença entre Concat() e Union() ? Quando usar Concat() e quando usar Union() ? Somente pode ser usado em list ? ...

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *