Spring Data Rest é vida

Fala aí galera, tudo bem? Meu nome é Fernando e sou instrutor e desenvolvedor na caelumalura.

Não é de hoje que o Spring vem facilitando nossa vida, e vamos combinar que os caras mandam muito bem!

Agora imagina só um mundo perfeito, onde declaramos nossas classes de modelo, repositórios e magicamente temos enpoints para fazermos todas as operações que declaramos nos nossos repositórios (por exemplo incluir, listar, alterar e remover).

Esse mundo perfeito existe e nele essa mágica se chama Spring data rest. Sim, ao utilizarmos ele só de declararmos nossos repositórios temos endpoints disponíveis.

Tá, legal saber que existe o Spring Data Rest.  Mas e como uso ele? (Show me the code!!!)

Bom galera eu montei um exemplo bem básico com Spring Boot Maven e está no meu Github. Nele você vai ver no arquivo pom.xml as seguintes declarações:

Spring Boot

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.4.2.RELEASE</version>
</parent>

E os staters do Spring Data e do Spring data Rest

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Além dos Starters tenho também o h2 como banco de dados em memória e o lombok.

<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
</dependency>

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>

Para quem não conheçe o lombok, ele nos ajuda a escrever menos código repetitivo por exemplo: getter, setter, construtor, toString, equals/hashcode e etc...

Com ele anotamos nossos atributos com @Getter e/ou @Setter, anotamos nossa classe com @NoArgsConstructor ou @RequiredArgsConstructor, @AllArgsConstructor e ao compilarmos nosso código ele gera os byte-codes equivalentes.Vale a pena dar uma conferida!

No meu caso estou usando a anotação @Data (que engloba um monte dessas anotações) e @NoArgsConstructor (para ter um construtor sem parâmetros, por conta dos frameworks).

(OBS: Para rodar o projeto por dentro do IDE você precisa instalar o agent lombok, do contrário seu IDE não vai encontrar o getters, setters e afins.)

Em java temos duas classes de modelo Book e Author.

@Entity
@Data
@NoArgsConstructor
public class Author {

	@Id
	@GeneratedValue
	private Long id;

	@NotBlank
	private String name;

}
@Entity
@Data
@NoArgsConstructor
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	@NotBlank
	private String title;

	@NotBlank
	private String description;

	@Min(50)
	private Integer numOfPages;

	@Valid
	@ManyToMany(fetch=FetchType.EAGER)
	private Set<Author> authors = new HashSet<>();

	public void add(Author author) {
		authors.add(author);
	}

}

Além do modelo tenho também um repositórios para cada, BookRepository e AuthorRepository

public interface BookRepository extends CrudRepository<Book, Long> {
}
public interface AuthorRepository extends CrudRepository<Author, Long>{
}

Para subir a aplicação tenho a classe Application que inicia o Spring Boot e salva um livro e um autor no banco de dados;

@SpringBootApplication
public class Application {

	public static void main(String[] args) throws Exception {
		SpringApplication.run(Application.class, args);
	}

	@Autowired
	private BookRepository bookRepository;

	@Autowired
	private AuthorRepository authorRepository;

	@PostConstruct
	@Transactional
	public void onLoad(){

		Author alberto = new Author();
		alberto.setName("Alberto");
		authorRepository.save(alberto);

		Book book = new Book();
		book.setTitle("Spring MVC");
		book.setDescription("Domine o principal framework web Java");
		book.setNumOfPages(237);

		book.add(alberto);

		bookRepository.save(book);

	}

}

E pronto! Temos uma endpoints para efetuarmos o CRUD de livros e autores. (Spring Data Rest é ou não é vida?)

Ao acessarmos http://localhost:8080, temos o seguinte retorno

{
"_links" : {
"authors" : {
"href" : "http://localhost:8080/authors&quot;
},
"books" : {
"href" : "http://localhost:8080/books&quot;
},
"profile" : {
"href" : "http://localhost:8080/profile&quot;
}
}
}

O Spring Data Rest usa fortemente os conceitos de HATEOAS que a grosso modo diz que através de um recurso disponível deve ser possível navegar pelo estado desse recurso através dos links. Por padrão o spring usa um modelo de apresentação chamado HAL para representação da resposta.

Bom os endpoints /authors e /books me parecem bem ok, o que deve ter neles, mas e o endpoint /profile?

Esse enpoint tem as meta-informações do seus serviços, por exemplo ao acessarmos http://localhost:8080/profile/authors temos o seguinte resultado

{
"alps":{
"version":"1.0",
"descriptors":[
{
"id":"author-representation",
"href":"http://localhost:8080/profile/authors&quot;,
"descriptors":[
{
"name":"name",
"type":"SEMANTIC"
}
]
},
{
"id":"create-authors",
"name":"authors",
"type":"UNSAFE",
"rt":"#author-representation"
},
{
"id":"get-authors",
"name":"authors",
"type":"SAFE",
"rt":"#author-representation"
},
{
"id":"patch-author",
"name":"author",
"type":"UNSAFE",
"rt":"#author-representation"
},
{
"id":"update-author",
"name":"author",
"type":"IDEMPOTENT",
"rt":"#author-representation"
},
{
"id":"get-author",
"name":"author",
"type":"SAFE",
"rt":"#author-representation"
},
{
"id":"delete-author",
"name":"author",
"type":"IDEMPOTENT",
"rt":"#author-representation"
}
]
}
}

Essas meta-informações estão associadas a qual representação temos no enpoint (no nosso caso)  authors e toda a definição dessa representação (os atributos e métodos). O endpoint profile é definido na RFC 6906.

Dentro da representação do author temos um objeto com id author-representation e nele temos um descritor com nome name e o tipo dele é SEMANTIC.
Esse tipo SEMANTIC indica um elemento que guarda um estado (atributo).

Além desse objeto temos um objeto com id create-authors, get-authors ou update-author ambos com nome author porém com tipos diferentes (nesse caso temos UNSAFE, SAFE e IDEMPOTENT). Esse objetos são as operações que podemos fazer sobre essa representação (methods).

Cada tipo tem um comportamento diferente:

SAFE – operações desse tipo são idempotentes e não alteram estado no servidor (GET, HEAD).

UNSAFE – operações desse tipo não são idempotentes e alteram estado no servidor (POST).

IDEMPOTENT – operações desse tipo são idempotentes e alteram estado no servidor (PUT, DELETE).

Uma operação é considerada idempotente se o resultado dele for bem sucedida indiferente do numero de vezes que ela for executada.

Agora sim, vamos para os endpoints mais “óbvieis“. Irei utilizar o programa curl para efetuar as chamadas para nosso endpoints.

Para listar nosso autores podemos efetuar a seguinte requisição:

curl -X GET http://localhost:8080/authors

(Deixei explicito o verbo GET somente por questões didáticas).

Nesse caso temos o seguinte resultado:

{
"_embedded" : {
"authors" : [ {
"name" : "Alberto",
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors/1&quot;
},
"author" : {
"href" : "http://localhost:8080/authors/1&quot;
}
}]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors&quot;
},
"profile" : {
"href" : "http://localhost:8080/profile/authors&quot;
}
}
}

Nesse caso temos um autor com o nome Alberto e os links do autor levam ao recurso dele mesmo. E os links da representação como um todo levam a lista de autores ou ao profile de autores.

Vamos seguirmos o link para listar somente o autor Alberto.

curl -X GET http://localhost:8080/authors/1

E nesse caso temos o seguinte resultado:

{
"name" : "Alberto",
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors/1&quot;
},
"author" : {
"href" : "http://localhost:8080/authors/1&quot;
}
}
}

E para criar um novo autor? bom para isso temos que usar o verbo POST.

curl -X POST -H "Content-type: application/json" -d '{"name": "Fernando"}' http://localhost:8080/authors

Agora temos dois autores, e para alterar? Fácil né, só usar outro verbo. Nesse caso o PATCH

 curl -X PATCH -H "Content-type: application/json" -d '{"name": "Fernando Willian"}' http://localhost:8080/authors/2

E para excluir vamos usar o verbo DELETE

curl -X DELETE http://localhost:8080/authors/2

Podemos usar as mesmas operações para os livros.

Para deixar um pouco mais interessante, vamos adicionar um pouco de segurança ao nossos endpoints.
Para isso vamos adicionar o starter do Spring Security no nosso arquivo pom.xml:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

E vamos configurar como será nossa autenticação e quais os endereços que queremos proteger:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
		.withUser("common").password("123").roles("USER").and()
		.withUser("admin").password("@dm1n").roles("ADMIN");
		
	}
	
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.httpBasic()
			.and()
				.authorizeRequests()
					.antMatchers(HttpMethod.POST, "/**").hasRole("ADMIN")
					.antMatchers(HttpMethod.PATCH, "/**").hasRole("ADMIN")
					.antMatchers(HttpMethod.DELETE, "/**").hasRole("ADMIN")
				.and()
					.csrf()
						.disable();
	}
	
}

Nessa configuração temos que nos autenticar com uma conta com permissão de ADMIN para efetuar qualquer requisição com os verbos POST, PATCH e DELETE.

Agora para incluir um novo autor devemos nos autenticar:

curl -X POST -H "Content-type: application/json" -d '{"name": "Fernando"}' http://localhost:8080/authors -u admin:@dm1n

O mesmo se aplica para alteração ou exclusão.

E aí o que achou do Spring Data Rest? Já o conhecia?

Access Control List: Protegendo o acesso a seus objetos

O Spring Security é um dos módulos mais usados do Spring e sem dúvida cumpre o seu papel muito bem. Ele é bastante usado para restringirmos o acesso aos métodos e urls da nossa aplicação baseado nos perfis dos usuários do sistema. Uma situação mais complicada pode aparecer quando dois usuários possuem um perfil que permite o acesso a uma mesma url, por exemplo um endereço que exibe o detalhe de uma prova dele em um sistema de provas online. Segue abaixo o exemplo:

  /prova/resolucao/1

E se o outro usuário logado também tentar acessar o mesmo endereço de detalhe? Como você vai negar o acesso? Provavelmente acabaríamos escrevendo um código parecido com o que segue.

	Usuario usuarioLogado = ...;
	ResolucaoProva resolucao = resolucaoProvaDao.carrega(idResolucao);
	if(!resolucao.pertence(usuarioLogado)){
		throw new SemAutorizacaoException("espertinho...");
	}

Essa é uma solução perfeitamente válida. O ponto negativo é que você não vai reaproveitar para os outros sistemas. Além do mais, esse sistema de permissões pode ficar mais complexo com usuários podendo ver detalhes de outros usuários, mas sem possibilidade edição por exemplo. Tudo vai depender do tipo de aplicação que você estiver desenvolvendo.

Para amenizar essa complexidade, o Spring Security tem um módulo chamado de ACL(Access Control List). O objetivo dele é justamente generalizar essa implementação nos dando a possibilidade de aplicar regras de acesso para qualquer tipo de objeto! Essa semana eu e Rafael Rollo utilizamos ele em um projeto dentro da Caelum. O objetivo do post é justamente mostrar o código necessário para deixar tudo funcionando. Respire bastante, porque não é pouco código não.

Vamos começar pela parte mais interessante, pelo menos para mim, que é a de inserir as permissões de acesso para determinado objeto. Dê uma olhada no código abaixo:

	@Autowired
	private Permissoes permissoes;

	...

	@RequestMapping(value = "/finaliza", method = RequestMethod.POST)
	public String finaliza(HttpSession session,@AuthenticationPrincipal 
				Usuario usuarioLogado) {

		ResolucaoFinal resolucaoFinal = resolucaoEmAndamento
				.geraResolucaoFinal();
		resolucaoFinalDao.save(resolucaoFinal);

		//essa classe isola o código de adicionar permissões
		permissoes.adiciona(usuarioLogado,resolucaoFinal,BasePermission.READ);

		return "redirect:/resolucao/finalizada";
	}
	@Service
	public class Permissoes {
		
		@Autowired
		private MutableAclService mutableAclService;	

		public void adiciona(UserDetails user, Object objeto, 
                      Permission... permissoes) {
	        ObjectIdentity identity = new ObjectIdentityImpl(objeto);
	        MutableAcl acl = mutableAclService.createAcl(identity);
	        PrincipalSid principalSid = new PrincipalSid(user.getUsername());
	        for (Permission permissao : permissoes) {
	        	acl.insertAce(acl.getEntries().size(), permissao, principalSid, true);			
			}
	        mutableAclService.updateAcl(acl);	
		}

	}

Vamos focar na classe Permissoes, já que nela está acontecendo a parte que nos interessa. A classe MutableAclService é a nossa porta de entrada para criar, atualizar e ler as permissões de acesso a um objeto. Tanto que nesse pequeno trecho de código usamos dois métodos dela:

  1. createAcl
  2. updateAcl

Vamos analisá-los por partes, começando pelo createAcl. 

		public void adiciona(UserDetails user, Object objeto, 
                        Permission... permissoes) {
	        ObjectIdentity identity = new ObjectIdentityImpl(objeto);
	        MutableAcl acl = mutableAclService.createAcl(identity);
                ...

O createAcl espera como argumento um ObjectIdentity, que é justamente a representação do objeto que vai ter o acesso controlado. O retorno deste método é um objeto do tipo MutableAcl, a abstração para as permissões de um objetoComo você já deve ter notado, o construtor do ObjectIdentity recebe um Object como parâmetro, já que qualquer tipo de objeto pode ser protegido. Um ponto importante é que esse Object deve ter um método chamado getId, cujo retorno será usado como chave estrangeira nas tabelas que serão criadas(ainda vamos comentar sobre isso). Caso sua classe não possua um método chamado getId, você pode usar o outro construtor que recebe além do objeto, um id como argumento.

Para falarmos do updateAcl, precisamos primeiro dar uma olhada no método insertAcl da classe MutableAcl. Através dele é que vamos adicionando permissões para o nosso objeto.

	        MutableAcl acl = mutableAclService.createAcl(identity);
	        PrincipalSid principalSid = new PrincipalSid(user.getUsername());
	        for (Permission permissao : permissoes) {
	           acl.insertAce(acl.getEntries().size(), permissao, principalSid, true);			
		}

Para ser bem sincero, essa assinatura de método é um pouco estranha para mim :/. Vamos começar pelos argumentos que fazem mais sentido, o segundo e o terceiro. O segundo argumento é um objeto cuja classe implementa a interface Permission, que é a abstração de uma permissão dentro do ACL. Ele já tem uma implementação base que é tranquila para a gente usar.

	public class BasePermission extends AbstractPermission {
		public static final Permission READ = new BasePermission(1 << 0, 'R'); // 1
		public static final Permission WRITE = new BasePermission(1 << 1, 'W'); // 2
		public static final Permission CREATE = new BasePermission(1 << 2, 'C'); // 4
		public static final Permission DELETE = new BasePermission(1 << 3, 'D'); // 8
		public static final Permission ADMINISTRATION = new BasePermission(1 << 4, 'A'); // 16

		protected BasePermission(int mask) {
			super(mask);
		}

		protected BasePermission(int mask, char code) {
			super(mask, code);
		}
	}

Caso você esteja questionando o motivo da permissão ser representada por um int, bem vindo ao time :), neste link eles discutem sobre a decisão. O terceiro argumento do método é justamente o dono do objeto, que é representado pela classe PrincipalSid(Principal Security Identity).  Passamos como argumento no construtor dele um identificador desse “dono” que existe dentro do nosso sistema, no meu caso foi o email do usuário.

Pensando de maneira direta, os argumentos da permissão e do dono daquela permissão deveriam ser suficientes para definir a regra de acesso ao objeto, só que ainda temos outros dois. Vou tentar ser breve aqui, já que para mim eles não fazem muito sentido. A classe MutableAcl possui um outro método chamado updateAce, cuja assinatura segue abaixo:

    void updateAce(int aceIndex, Permission permission) throws NotFoundException;

Perceba que o primeiro argumento é um índice que é necessário para localizar a posição da permissão. Como para atualizar precisa do índice, para inserir você também precisar passar qual é a posição que você deseja adicionar a nova permissão. Essa é a razão do primeiro argumento do método insertAce ser um inteiro. O último argumento é um boolean para indicar se você realmente quer dar aquela permissão de acesso ao objeto. O que eu não entendo é: se você não quer dar permissão de acesso, não é melhor simplesmente não adicionar? Sei lá, devo estar me passando em alguma coisa aqui.

Com as permissões inseridas no objeto do tipo MutableAcl, podemos invocar o updateAcl da classe MutableAclService, para que tudo seja refletido no banco de dados.

Uma vez que as permissões estão associadas, você deve estar se perguntando como vamos proteger o acesso ao método. Dê uma olhada no código abaixo.

	@RequestMapping(value = "/prova/resolucao/{id}", method = RequestMethod.POST)
	@PreAuthorize("hasPermission(#id,'br.com.caelum.provas.models.ResolucaoFinal','read')")
	public String visualiza(@PathVariable("id") Integer id) {
		ResolucaoFinal resolucao = resolucaoDao.carrega(id);
		...
	}

Aqui fizemos uso das expressões suportadas pelo Spring. Por sinal esse é um tema que merece um post aqui no blog :). Temos acesso a função hasPermission que recebe três argumentos.

  1. O id objeto que está tentando ser acessado. Repare que usamos o #” para indicar que ele deve pegar esse valor do argumento de mesmo nome.
  2. A classe do objeto que ele precisa carregar.
  3. O nome da permissão que o usuário logado precisa ter. Esse nome precisa ser o mesmo da constante usada na hora de adicionar a permissão. Por default o ACL vai fazer o carregamento da permissão baseado nessa condição.

O Spring Security ACL vai verificar se o usuário logado tem acesso especificamente a este objeto. Essa é uma solução super genérica e que você pode aproveitar para qualquer projeto seu!

Para fechar, precisamos realizar as configurações necessárias para que tudo funcione. Essa parte toda eu vou deixar isolada em uma classe específica.

	@Configuration
	public class ConfiguracaoACL {

		@Autowired
		private DataSource dataSource;

		@Bean
		public AclAuthorizationStrategy aclAuthorization() {
			AclAuthorizationStrategyImpl aclAuthorizationStrategy = new AclAuthorizationStrategyImpl(
					new SimpleGrantedAuthority("ROLE_ADMIN"));				
			return aclAuthorizationStrategy;
		}

		@Bean
		public PermissionGrantingStrategy permissionGrantingStrategy() {
			PermissionGrantingStrategy grantingStrategy = new DefaultPermissionGrantingStrategy(
					new ConsoleAuditLogger());
			return grantingStrategy;
		}

		@Bean	
		public Ehcache ehCache() {
			CacheManager cacheManager = CacheManager.create();
			if (!cacheManager.cacheExists("aclCache")) {
				cacheManager.addCache("aclCache");
			}

			Ehcache ehCache = cacheManager.getEhcache("aclCache");
			return ehCache;
		}
			

		@Bean
		public AclCache aclCache(Ehcache ehCache, PermissionGrantingStrategy grantingStrategy,
				AclAuthorizationStrategy aclAuthorizationStrategy) {
			AclCache aclCache = new EhCacheBasedAclCache(ehCache, grantingStrategy,
					aclAuthorizationStrategy);

			return aclCache;
		}

		@Bean
		public BasicLookupStrategy basicLookupStrategy(AclCache aclCache,
				AclAuthorizationStrategy aclAuthorizationStrategy,
				PermissionGrantingStrategy grantingStrategy) {

			return new BasicLookupStrategy(dataSource, aclCache, aclAuthorizationStrategy,
					grantingStrategy);
		}

		@Bean
		public MutableAclService mutableAclService(BasicLookupStrategy lookupStrategy, AclCache aclCache) {
			JdbcMutableAclService service = new JdbcMutableAclService(dataSource, lookupStrategy,
					aclCache);
			service.setClassIdentityQuery("select last_insert_id()");
			service.setSidIdentityQuery("select last_insert_id()");
			return service;
		}

		@Bean
		public MethodSecurityExpressionHandler expressionHandler(PermissionEvaluator permissionEvaluator) {
			DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler();
			handler.setPermissionEvaluator(permissionEvaluator);
			return handler;
		}

		@Bean
		public PermissionEvaluator permissionEvaluator(MutableAclService aclService) {
			AclPermissionEvaluator aclPermissionEvaluator = new AclPermissionEvaluator(aclService);
			return aclPermissionEvaluator;
		}

	}

Aqui você precisa medir seus esforços. Não se pode negar que cada um desses métodos produz um objeto que é necessário para o funcionamento, mas não acho saudável gastar vários parágrafos explicando um por um. Vou deixar uma lista apenas com uma breve ideia :).

  1. LookupStrategy: dependência do JdbcMutableAclService para fazer buscas no banco pelas permissões.
  2. AclCache: Camada de cache para não ficar indo no banco o tempo todo.
  3. AclAuthorizationStrategy: Verifica se certa role pode alterar permissões de um objeto.
  4. PermissionGrantingStrategy: Verifica se certo usuário pode realizar uma operação sobre o objeto.
  5. PermissionEvalutaor: Necessária para suportar o hasPermission nos métodos.

Como é de praxe, aconselho que você olhe o Javadoc de todas elas, para saber exatamente do papel de cada uma. Aproveitando, já vou fazer uma atualização no SetupMyProject para suportar a configuração do ACL :). Para completar é necessário criar as tabelas necessárias para que o módulo do ACL funcione e, além disso, modificar sua classe de configuração do Spring Security e adicionar a annotation @EnableGlobalMethodSecurity(prePostEnabled=true). Essa annotation habilita a checagem da annotation @PreAuthorize :).

Para você não esquecer, também deixo as dependências que precisam ser adicionadas no pom.xml.

		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-acl</artifactId>
		</dependency>
		<!-- cache -->

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>

		<dependency>
			<groupId>net.sf.ehcache</groupId>
			<artifactId>ehcache</artifactId>
		</dependency>

Pronto! O post foi um pouco longo, mas também o uso e configuração dele não é lá uma das coisas mais fáceis. E esse foi só o primeiro deste ano, se preparem para mais. Também não deixem de mandar sugestões para temas, por aqui ou me referenciando no twitter.

Será que você sabe tudo das configurações do seu projeto com Spring?

O objetivo do post é esclarecer alguns detalhes de configuração providos pelo Spring que, talvez, não seja claro para todo mundo que usa o ecossistema oferecido pelo framework. Os três primeiros parágrafos cobrem o tópico relativo a annotation @Configuration. Depois cada um cobre um tema diferente, então fique a vontade para ler na ordem que você quiser :).

O primeiro ponto é o uso da annotation @Configuration. A ideia é que você minimize a quantidade de código necessário para ensinar ao Spring quais classes do seu sistema são responsáveis  por criar objetos que serão gerenciados pelo framework. Vamos supor que você está numa aplicação web, usando o Spring MVC. Geralmente você vai ter uma classe de configuração relativa ao Servlet em si, como está exibido no código abaixo.

    public class ServletSpringMVC extends
		AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] {AppWebConfiguration.class};
	}

        ...

    }

Como exemplificado, a única classe que você precisa para o método getRootConfigClasses(que vai ser explicado em outro parágrafo) é a que representa a “entrada” das suas configurações. Dê uma olhada no exemplo abaixo:

      @EnableWebMvc
      @ComponentScan(basePackages="br.com.casadocodigo.loja")
      @EnableCaching
      public class AppWebConfiguration extends WebMvcConfigurerAdapter {

	@Bean
	public LocaleResolver localeResolver() {
		return new CookieLocaleResolver();
	}

     }

Perceba que essa classe já define alguns métodos anotados com @Bean, mas também está anotada com a @ComponentScan indicando o pacote base que deve ser varrido. É justamente aí que entra a annotation @Configuration. Durante a varredura, o Spring vai procurar por qualquer classe anotada com ela ou com @Component e, caso encontre, vai carregar essa classe e já invocar todos os métodos anotados com @Bean.  Lembre sempre disso, você não precisa adicionar todas as classes que servem de configuração! Outro ponto importante é: se a classe é de configuração, anote ela com @Configuration e não com @Component. Semântica é muito importante em qualquer sistema.

O segundo tópico tem a ver com o escopo de criação dos objetos que são gerenciados pelo container do Spring. Por default, todos os beans vivem no escopo de aplicação. Isso quer dizer que uma vez que o objeto foi criado, a mesma instância será utilizada por todo tempo de vida da aplicação. Não importa a classe que você esteja anotando, pode ser um @Repository, @Service ou @Controller.  O que você precisa lembrar é: caso queira criar um atributo nessa classe que não seja gerenciado pelo Spring, tenha em mente que ele será compartilhado por todo mundo na sua aplicação. Caso você queira mudar o escopo, como a maioria já deve saber, basta usar a annotation @Scope.  Na minha humilde opinião, o escopo default dos controllers deveria ser o de request. Caso você se interesse por isso, criei um exemplo que aplica esse escopo em todo mundo anotado com @Controller.

Essa é para a galera que usa o Spring Security. Quando você usa a parte de segurança, já vem habilitada a proteção contra o CSRF. Quando usamos as tags de formulário do Spring MVC, meio que por magia, começa a aparecer um campo hidden com o nome _csrf. Sabemos que a configuração do Spring Security faz esse campo aparecer, mas como? Caso você dê uma olhada na classe WebMvcSecurityConfigurationverá que ela possui um método que retorna um objeto do tipo RequestDataValueProcessor. Você pode criar implementações dessa interface para adicionar campos no formulário que usam a tag form do Spring MVC. Eu por exemplo já fiz uso, quando precisei adicionar o hidden com o estado do wizard do SetupMyProject. Isso é uma característica que eu acho muito legal no Spring MVC, como o framework tem muito feedback da comunidade, a arquitetura dele foi realmente pensada para resolver desde os problemas mais comuns nas aplicações web até as situações mais específicas.

A última configuração vem da classe responsável pela configuração da Servlet, a que geralmente herda de AbstractAnnotationConfigDispatcherServletInitializer. Existem dois métodos que você pode usar para adicionar a classe de configuração, o getRootConfigClasses e o getServletConfigClasses. Resumindo, você pode usar sempre o getRootConfigClasses. A diferença é que usando o primeiro as suas configurações são carregadas dentro de um Listener da especificação de Servlet, especificamente uma instância do objeto do tipo ContextLoaderListener. Enquanto que usando a segunda opção, suas configurações são carregadas dentro do DispatcherServlet do Spring MVC.  Usar o primeiro é até melhor, já que você garante que as configurações estarão carregadas em qualquer circunstância.

Será que você realmente está protegendo suas urls?

Esse é um post rápido, mas pode ser útil para quem utiliza o Spring Security em suas aplicações. Não sei se você já percebeu, mas para cada url mapeada através de um @RequestMapping, por default, você também tem a possibilidade de acessar variações dela. Veja o seguinte exemplo:

	@RequestMapping("/produtos/form")
	public ModelAndView form(@ModelAttribute Product product){
		ModelAndView modelAndView = new ModelAndView("products/form");
		modelAndView.addObject("bookTypes", BookType.values());
		return modelAndView;
	}

Além de poder acessar o endereço a partir da url mapeada, também podemos acessar alguma variações.

  • /produtos/form/
  • /produtos/form.

Parece inofensivo, não? Agora e se você tiver utilizado o Spring Security para proteger essa url?

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()		
		.antMatchers("/produtos/form").hasRole("ADMIN")
                //outras configurações

	}

Nessa situação, caso a pessoa tente acessar o endereço protegido usando uma das variações, ela vai conseguir! Caso você queira se proteger disso através do próprio Spring Security, pode alterar a configuração de url e usar uma regex, ao invés do AntMatcher.

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
		.regexMatchers("/produtos/form[/\\.]?").hasRole("ADMIN")
		//outras configurações

Uma outra abordagem é fazer a servlet do Spring MVC ignorar os caminhos que não baterem exatamente com os configurados.

	@Override
	public void configurePathMatch(final PathMatchConfigurer configurer) {
		configurer.setUseSuffixPatternMatch(false);
		configurer.setUseTrailingSlashMatch(false);
	}

Basta que você sobreescreva o método configurePathMatch e invoque os métodos acima. A classe PathMatchConfigurer é justamente a responsável por guardar essas configurações. Tome apenas um cuidado, no momento em que você passa a ignorar tudo que vem depois do caminho configurado, você deixou de ter o suporte as facilidades para devolver outros tipos, que não o HTML, por exemplo quando você quer retornar json usando o sufixo .json. Agora você sempre se apoiará no cabeçalho accept do HTTP. Já tinha passado por essa situação? Como você tinha resolvido?

 

Spring Security, Spring JPA e o JAVAEE

Quando pensamos em adicionar a parte de segurança dentro da nossa aplicação, a primeira solução que vem a nossa cabeça é usar o Spring Security. Por sinal é um pensamento que faz todo sentido, já que hoje em dia ele é o projeto de segurança mais completo do mercado. Só que quando estamos no mundo JAVAEE, já existe uma especificação responsável por essa área, a JAAS. Colocando as duas lado a lado, não é complexo perceber que o Spring Security é uma solução bem mais moderna, com uma arquitetura mais bem bolada e com pontos de extensões mais definidos. Além disso é muito mais sincronizado com a realidade da maioria dos projetos. Queremos fazer segurança baseada em roles urls e ter uma maneira fácil de fazer essa associação é um grande diferencial. Este post é para você, que está numa aplicação JAVAEE, mas tem o desejo de usar o Spring Security como framework de segurança. Vou assumir que o leitor já sabe o básico do framework.

A configuração inicial não muda muito, supondo que você usa Maven, é necessário adicionar as novas dependências no pom.xml. 

		<!-- spring -->

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

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>4.1.0.RELEASE</version>
		</dependency>

O primeiro ponto diferente é em relação a configuração do filtro. Normalmente basta que você declare uma classe que herde AbstractSecurityWebApplicationInitializer e pronto. Só que como não estamos usando nada além do Spring, precisamos mudar um pouco código e passar todas as nossas classes de configuração que devem ser carregadas.

   public class SpringSecurityFilterConfiguration extends
		AbstractSecurityWebApplicationInitializer {

	public SpringSecurityFilterConfiguration() {
		super(SecurityConfiguration.class,SystemUserDAO.class,JPAConfiguration.class);
	}
   }

Geralmente você não precisa fazer isso, porque este passo já foi realizado na classe de configuração do Spring MVC. A classe SecurityConfiguration não tem nada demais, você pode acessá-la seguindo o link. Ela basicamente define as regras de segurança, da mesma forma que seria feito em um projeto usando unicamente a stack do Spring. A classe SystemUserDAO também não tem nada demais, ela implementa a interface UserDetailsService e precisa de um EntityManager injetado. E agora sim, temos uma situação. Qual EntityManager deve ser injetado? Lembre que estamos dentro de um servidor de aplicação e o melhor que usemos o contexto de persistência provido pelo container. Para a nossa sorte, o Spring é bastante extensível e não tem a menor pretensão de ser apenas usado isolado de todo mundo.

Como já temos um persistence.xml configurado, integrado com a JTA e utilizando o DataSource configurado no servidor, precisamos apenas ensinar ao Spring que queremos tirar proveito de todas essa configuração. Para isso vamos dar uma olhada na classe JPAConfiguration.

	package br.com.casadocodigo.security;

	//imports

	import org.springframework.context.annotation.Bean;

	public class JPAConfiguration {

		@Bean
		public EntityManagerFactory emf() throws NamingException {
			Context ctx = new InitialContext();
			EntityManagerFactory lookup = (EntityManagerFactory) ctx
					.lookup("java:comp/env/persistence/casadocodigo-emf");
			return lookup;
		}
	}

Simplesmente buscamos a referência para a EntityManagerFactory a partir da JNDI. Uma especificação muito usada no mundo Java, onde conseguimos associar nomes a objetos que podem ser recuperados no servidor. Uma pergunta que pode estar na sua mente é: de onde veio esse nome? Para isso vamos usar outro recurso da especificação JAVAEE, nesse caso a de Servlets. Conseguimos expor a EntityManagerFactory na JNDI, a partir do nosso arquivo web.xml.

	<persistence-unit-ref>
		<persistence-unit-ref-name>persistence/casadocodigo-emf</persistence-unit-ref-name>
		<persistence-unit-name>casadocodigo-persistence-unit</persistence-unit-name>
	</persistence-unit-ref>

A tag persistence-unit-ref-name define o nome que você quer expor na JNDI. Já a tag persistence-unit-name faz referência ao nome configurado no arquivo web.xml.

Pronto! Dessa forma conseguimos adicionar o Spring Security por exemplo, em um projeto usando JSF dentro de um servidor JAVAEE. Aproveitamos as configurações da JPA já feitas pela aplicação e simplesmente escrevemos a ponte para o Spring. Uma outra pergunta que pode ficar na cabeça é: o servidor JAVAEE já tem o CDI, só que o Spring faz a  mesma coisa. Não tem problema algum, não estamos querendo colocar o Spring para ser o controlador da Injeção de Dependências do sistema, estamos apenas usando o necessário para o Spring Security funcionar. Outra detalhe bem interessante é que a parte principal do Spring Security funciona dentro de um Filterpor isso conseguimos usar ele para fazer a segurança de qualquer aplicação web.

O Spring foi pensado justamente para ser extensível. É claro que o melhor dos mundos é usar os módulos do próprio Spring e ser feliz, mas isso nem sempre é possível. Também não fique preso ao estigma de usar Spring ou JAVAEE, use o que se encaixe melhor na sua aplicação. Encare tudo como ferramenta, nada de nutrir amor por tecnologia :).

URLs e Roles dinâmicas no Spring Security

Boa parte das pessoas que aprendem o Spring Security sempre se questionam o motivo da associação entre url role ser fixa no código.

	http.authorizeRequests()
	.antMatchers("/produtos/form").hasRole("ADMIN")
	.antMatchers("/shopping/**").permitAll()
	.antMatchers(HttpMethod.POST,"/produtos").hasRole("ADMIN")
	.antMatchers("/produtos/**").permitAll()
	.antMatchers(HttpMethod.GET, "/admin/users").permitAll()
	.anyRequest().authenticated()

O principal, até citado pela própria documentação do Spring, é que isso é uma parte importante do seu sistema e, portanto, deve ser testada para garantir que tudo esteja configurado da maneira correta. Quando você decide optar por uma configuração dinâmica, por exemplo carregando essas associações de um banco de dados, o sistema fica mais suscetível a erros, já que algum cadastro pode ter sido esquecido ou até ter sido feito de maneira errada. Para ser bem sincero, concordo com a documentação. Sem contar os fatos já explicados, ainda tem a parte que é mais complicado de fazer :). Busquei um pouco sobre o assunto no todo poderoso do conhecimento e não encontrei nada realmente matador, então resolvi escrever este post.

Podemos até combinar os dois meios de configuração, o estático e o dinâmico. Para facilitar, vamos simular que você deseja que todas associações entre roles e urls venham do banco. A primeira coisa que precisamos fazer é retirar as configurações estáticas.

	http.authorizeRequests()
	.anyRequest().authenticated()
	.and()
	.formLogin().loginPage("/login").permitAll()
	.and()
	.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"));

E agora, como vamos fazer? A classe responsável por checar as roles liberadas para cada url  é a FilterSecurityInterceptor. É justamente ela que precisa ser alterada. O melhor jeito de ter acesso a este objeto é pedindo para o Spring Security nos notificar quando ele tiver sido criado, pois dessa forma basta que façamos algumas pequenas alterações.

	http.authorizeRequests()
	.anyRequest().authenticated()
	.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
		public <O extends FilterSecurityInterceptor> O postProcess(
				O fsi) {
                        //podemos alterar o objeto aqui
			return fsi;
		}
	})
	.and()
	.formLogin().loginPage("/login").permitAll()
	.and()
	.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"));

O método withObjectPostProcessor deve ser usado sempre que você precisar processar algum objeto criado pela configuração, o que é meio raro, mas perfeito para o nosso caso. O objeto do tipo FilterSecurityInterceptor possui um método chamado setSecurityMetadataSource,  que recebe como argumento algum objeto que implementa a interface FilterInvocationSecurityMetadataSource. É justamente aqui que vamos entrar. Precisamos criar uma implementação dessa interface que carregue do banco de dados a associação entre roles e urls.

	.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
		public <O extends FilterSecurityInterceptor> O postProcess(
				O fsi) {
			fsi.setSecurityMetadataSource(dynamicSecurityMetadataSource);
			return fsi;
		}
	})

Agora vamos dar uma olhada na implementação que criamos.

	package br.com.casadocodigo.loja.conf;

	@Component
	public class DynamicSecurityMetadataSource implements
			FilterInvocationSecurityMetadataSource {

		@Autowired
		private SystemURLDAO systemUrls;;

		@Override
		public Collection<ConfigAttribute> getAttributes(Object object)
				throws IllegalArgumentException {
			final HttpServletRequest request = ((FilterInvocation) object)
					.getRequest();

			String urlWithoutContextPath = request.getRequestURI().substring(
					request.getContextPath().length());

			Optional<SystemURL> foundUrl = systemUrls
					.findByURL(urlWithoutContextPath);

			if (foundUrl.isPresent()) {
				return foundUrl.get().getRolesAllowed().stream()
						.map(this::configAttribute).collect(Collectors.toList());
			}

			return null;
		}

		private ConfigAttribute configAttribute(Role role) {
			return new ConfigAttribute() {
				@Override
				public String getAttribute() {
					return role.getAuthority();
				}
			};
		}

		@Override
		public Collection<ConfigAttribute> getAllConfigAttributes() {
			return null;
		}

		@Override
		public boolean supports(Class<?> clazz) {
			return FilterInvocation.class.isAssignableFrom(clazz);
		}

	}

A interface ConfigAttribute é a abstração de uma permissão. Por exemplo, quando usamos o modo default usamos a implementação WebExpressionConfigAttribute. No nosso caso, simplesmente usamos uma classe anônima que implementa a interface, mas também poderíamos ter criado uma classe chamada, por exemplo, de RoleConfigAttribute. 

Um último detalhe, muito importante. Quando uma nova requisição é feita, para o Spring Security decidir se o usuário pode acessar o recurso ou não, é usada uma implementação da interface AccessDecisionManager. O jeito mais fácil de entender o que acontece por debaixo dos panos, é dando uma olhada no código executado para liberar ou não um acesso. Vamos olhar, por exemplo, a implementação da classe AffirmativeBased.

	public void decide(Authentication authentication, Object object,
			Collection<ConfigAttribute> configAttributes) throws AccessDeniedException {
		int deny = 0;

		for (AccessDecisionVoter voter : getDecisionVoters()) {
			int result = voter.vote(authentication, object, configAttributes);

			if (logger.isDebugEnabled()) {
				logger.debug("Voter: " + voter + ", returned: " + result);
			}

			switch (result) {
			case AccessDecisionVoter.ACCESS_GRANTED:
				return;

			case AccessDecisionVoter.ACCESS_DENIED:
				deny++;

				break;

			default:
				break;
			}
		}

Ele faz um loop por implementações de AccessDecisionVoter e, neste caso, apenas verifica se alguma implementação libera o acesso. Existem outras implementações da interface AccessDecisionManager que decidem a liberação das urls baseadas em outras estratégias. Quando usamos as configurações estáticas, o único voter utilizado é o WebExpressionVoter. Só que, no nosso caso, precisamos de uma implementação baseada simplesmente em roles . Para isso que existe a classe RoleVoter. Apenas vamos ensinar ao Spring Security que queremos usar outro AcessDecisionManager.

	AffirmativeBased affirmativeBased = new AffirmativeBased(Arrays.asList(new RoleVoter());
	http.authorizeRequests().accessDecisionManager(affirmativeBased)
	.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
		public <O extends FilterSecurityInterceptor> O postProcess(
				O fsi) {
			fsi.setSecurityMetadataSource(dynamicSecurityMetadataSource);
			return fsi;
		}
	})
	.and()
	.formLogin().loginPage("/login").permitAll()
	.and()
	.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout"));

Pronto! Agora conseguimos verificar os acessos a partir de regras cadastradas no banco de dados. O código completo de exemplo pode ser encontrado nesse commit. Esse post foi um pouco mais avançado e já espera que você possua um certo conhecimento sobre o Spring Security. No meu livro eu dedico um capítulo inteiro para o Spring Security e o código fonte também pode ser encontrado no meu github.