Instalação do driver NVidia no Ubuntu 16.04 com kernel 4.6.*

Estava tendo problemas com a versão 340.96 do driver da NVidia quando compilava o kernel 4.6.4, no começo pensei ser problema de configuração no kernel, pois fazia anos que não fazia isso :), mas depois de algum tempo de pesquisa descobri que era um bug no driver da NVidia mesmo.

Então vamos lá: (Vou mostrar como fazer a instalação do zero, mas se você já tinha o driver funcionando, mas só quer saber como faze-lo funcionar no novo kernel, pode pular esta parte.)

Antes de tudo, remova tudo que você tinha instalado da nvidia:

sudo apt-get remove nvidia*
sudo apt-get autoremove

Atualize o repositório:

sudo apt-get update

Instale os pacotes necessários para a compilação do módulo da NVidia:

sudo apt-get install dkms build-essential linux-headers-generic

Adicione o modulo do nouveau na blacklist:

sudo vi /etc/modprobe.d/blacklist-nouveau.conf

adicione estas linhas:

blacklist nouveau
blacklist lbm-nouveau
options nouveau modeset=0
alias nouveau off
alias lbm-nouveau off

Desabilite o modulo nouveau do kernel com o comando abaixo:

echo options nouveau modeset=0 | sudo tee -a /etc/modprobe.d/nouveau-kms.conf
sudo update-initramfs -u

baixe o patch do driver da nvidia: https://git.archlinux.org/svntogit/packages.git/plain/trunk/linux-4.6.patch?h=packages/nvidia-340xx

salve em algum diretório como linux-4.6.patch

Reinicie o computador, na tela de login digite Ctrl+Alt+F1, vai entrar no terminal do linux, faça o login, entre no diretorio onde se encontra o driver e digite:

sudo chmod +x NVIDIA-Linux-x86_64-340.96.run

Pare o X-Server

sudo service lightdm stop

Agora vamos começar a brincadeira com a versão do driver 340.96 da NVidia. Como falei, tem um bug nele que impede de compilar no 4.6.*

Extraia o driver da NVidia

sudo ./NVIDIA-Linux-x86_64-340.96.run -x

Entre no diretorio que extraimos:

cd NVIDIA-Linux-x86_64-340.96

copie o arquivo linux-4.6.patch que baixamos para o diretório NVIDIA-Linux-x86_64-340.96 Aplique o patch:

patch -p1 < linux-4.6.patch

entre no diretório kernel (dentro de NVIDIA-Linux-x86_64-340.96) e digite:

make clean
make install

Após a compilação volte um nível (NVIDIA-Linux-x86_64-340.96) e digite:

./nvidia-installer

Para mim, a opção para registrar o modulo DKMS não funcionou, marquei como ‘No’ Terminado a instalação, reiniciei o computador e o driver estava configurado. Foi nítida a diferença de qualidade de gráfico do driver nouveau para o driver proprietário da NVidia.

É isso aí…

Instalando o Redmine no Debian

Uma dúvida comum de quem vai instalar o Redmine pela primeira vez no Debian é, qual tutorial seguir, tendo em vista que na internet existem vários tutoriais. Mas qual usar? Pensando nisso, fiz um passo a passo rápido e facil, para instalar o Redmine no Debian.

Primeiro de tudo, vamos atualizar a lista de aplicativos no repositório com apt-get update.

 
sudo apt-get update

Após isso, vamos instalar o MySQL (pode usar o PostgreSQL se preferir)

Instalar o MySQL

 
sudo apt-get install mysql-server

Ao instalar o MySQL, será pedido uma senha de root

MySQL

Instalar Apache2 e Redmine

 
sudo apt-get install apache2 libapache2-mod-passenger
sudo apt-get install redmine redmine-mysql

Após isso, será perguntado se você quer configurar um banco de dados ao redmine, escolha “Yes”

Configurar Banco de Dados do Redmine

Agora escolha o banco de dados, no nosso caso será o MySQL.

Escolher Banco de Dados

Digite a senha do usuário de root do MySQL que criamos anteriormente

Senha Banco de Dados

digite a senha da instancia do redmine e depois repita a senha.

Senha Instancia Redmine

Agora vamos configurar os arquivos para colocar o Redmine no ar.

Editar o arquivo /etc/apache2/mods-available/passenger.conf

 
sudo vi /etc/apache2/mods-available/passenger.conf 

adicionar PassengerDefaultUser www-data

deixar igual abaixo:

 
  PassengerDefaultUser www-data
  PassengerRoot /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini
  PassengerDefaultRuby /usr/bin/ruby
 
sudo ln -s /usr/share/redmine/public /var/www/html/redmine

instalar o Bundler

 
gem install bundler

Editar o arquivo /etc/apache2/sites-available/000-default.conf

 
sudo vi /etc/apache2/sites-available/000-default.conf

adicionar:

 
<Directory /var/www/html/redmine>
    RailsBaseURI /redmine
    PassengerResolveSymlinksInDocumentRoot on
</Directory>

Adicionar o passenger ao apache

 
sudo a2enmod passenger

Reiniciar apache2

 
sudo service apache2 restart

Agora é só acessar http://localhost/redmine, o usuário e senha default é: admin/admin

Apache POI - Alterando templates no Word!

Uma tarefa muito comum, porém chata é a leitura de templates do Microsoft Word e a alteração de palavras chaves personalizadas por um valor em específico. Há diversas formas de se fazer isso, hoje vou mostrar uma dessas formas, utilizando o Apache POI. Este código foi testado com as versões 3.10-FINAL e 3.12 do Apache POI. Devido a um Bug na versão 3.12 é necessário adicionar a biblioteca poi-scratchpad as dependências, caso contrário você receberá o erro:

error: cannot access Paragraph
            List<XWPFRun> runs = paragraph.getRuns();
                                          ^
  class file for org.apache.poi.wp.usermodel.Paragraph not found

Nas dependências do gradle adicione as seguintes bibliotecas:

compile group: 'org.apache.poi', name: 'poi', version: '3.12'
compile group: 'org.apache.poi', name: 'poi-ooxml', version: '3.12'
compile group: 'org.apache.poi', name: 'poi-scratchpad', version: '3.12'

O modelo

Vamos utilizar um modelo simples (o código do exemplo é para arquivos .docx), mas bastante comum. Um texto com uma tabela estática, para efeito de exemplo coloquei apenas duas variáveis no documento, #valor_parcela# e uma variável na tabela #valor_total#. Veja a imagem do modelo, abaixo:

Modelo

E o código?

Vamos lá, chega de enrolação e vamos ao código. Criei um método extractTemplate(InputStream stream, Map<String, String> properties) que recebe um stream do documento e um Map<String, String> com as chaves/valores que iremos substituir.

Você pode fazer o download do código fonte do exemplo aqui. (https://github.com/cadocruz/exemplo-apache-poi)

Abaixo dois métodos extras, um para configurar o tamanho da tabela e outro para configurar um paragrafo.

Remover Automatic Component Binding no ADF

Muitas vezes quando estamos desenvolvendo com Oracle ADF no JDeveloper, e estamos criando nossas views, o JDeveloper faz o binding automatico dos componentes da nossa view com nosso Manage Bean, e dependendo da complexidade da view o manage bean vai ficar entulhado de código inútil.

Mas há 2 formas de desativá-los para a view desejada.

  • Configuração no JDeveloper
  • Manual

Configuração no JDeveloper

Vamos ver como fazer pela própria configuração do JDeveloper.

Abrir a pagina jsf no modo Design. Ir no Menu Design

Menu Design

Abrir aba Component Binding e desmarcar a opção Auto Bind

Aba Component Binding

Clique OK.

Manual

O modo manual é muito mais rápido e facil, é só procurar pelo seguinte comentário no fonte da view:

 
    </af:document>
    <!--oracle-jdev-comment:auto-binding-backing-bean-name:backing_JSF_untitled1-->
</f:view>

e removê-lo. Geralmente fica no fim do código.

Pronto, removemos o “Automatic Component Binding” do ADF.

JHipster: Java + AngularJS

Introdução ao JHipster: Spring Boot + AngularJS

Hoje vou falar um pouco do JHipster ou “Java Hipster” (como os criadores costumam chamar), é um gerador do Yeoman para criação de aplicações modernas em Java, com Spring Boot e AngularJS. Ele possibilita a utilização de tecnologias como Spring Boot, Spring MVC REST, JPA, segurança com Spring Security e bancos de dados como MySQL, PostgreSQL, MongoDB e Cassandra, entre outras.

Em pouco tempo, o JHipster vem se tornando muito popular no Github, e tem sido destaque de revistas online como InfoQ, Infoworld e SD Times, e também em conferências em Paris, Londres, Montreal, Omaha, Taipei , Richmond e Frankfurt.

Instalação

Vamos lá, para instalar o JHipster você precisa do Java instalado, Git e Node.js (para usar o gerenciador de pacotes do node, npm).

  • Instalar o Yeoman: npm install -g yo
  • Instalar o Bower: npm install -g bower
  • Instalar o Grunt: npm install -g grunt-cli, se preferir pode instalar o Gulp: npm install -g gulp.
  • E finalmente instalar o JHipster: npm install -g generator-jhipster

Criando um projeto

Crie um diretório onde ficará o projeto, e dentro do diretório que você acabou de criar, digite o comando yo jhipster.

a saída do comando é igual a abaixo:

 

 _     _   ___   __  _____  ____  ___       __  _____   __    __    _    
| |_| | | | |_) ( (`  | |  | |_  | |_)     ( (`  | |   / /\  / /`  | |_/ 
|_| | |_| |_|   _)_)  |_|  |_|__ |_| \     _)_)  |_|  /_/--\ \_\_, |_| \ 
                             ____  ___   ___                             
                            | |_  / / \ | |_)                            
                            |_|   \_\_/ |_| \                            
              _    __    _       __        ___   ____  _      __        
             | |  / /\  \ \  /  / /\      | | \ | |_  \ \  / ( (`       
           \_|_| /_/--\  \_\/  /_/--\     |_|_/ |_|__  \_\/  _)_)       


Welcome to the JHipster Generator

? (1/13) What is the base name of your application? (jhipster) 

Após isso é só ir respondendo as questões relativas ao seu projeto. Dependendo da sua escolha, as perguntas podem mudar.

  • ? (1/13) What is the base name of your application? mywebapp
    Aqui informo o nome da aplicação
  • ? (2/13) What is your default Java package name? br.com.cadocruz
    O nome do pacote padrão no Java
  • ? (3/13) Do you want to use Java 8? Yes (use Java 8)
    Aqui podemos escolher entre usar o Java 8 ou Java 7
  • ? (4/13) Which *type* of authentication would you like to use? HTTP Session Authentication (stateful, default Spring Security mechanism)
    O tipo de autenticação que vamos usar, as opções são: HTTP Session Authentication, OAuth2 Authentication e Token-based authentication
  • ? (5/13) Which *type* of database would you like to use? SQL (H2, MySQL, PostgreSQL)
    O tipo de banco de dados vamos usar: SQL (H2, MySQL, PostgreSQL), MongoDB ou Cassandra
  • ? (6/13) Which *production* database would you like to use? MySQL
    Como escolhi SQL na pergunta anterior, aqui escolho qual banco usar em produção MySQL ou PostgreSQL
  • ? (7/13) Which *development* database would you like to use? H2 in-memory with Web console
    Aqui semelhante a pergunta acima, mas para o ambiente de desenvolvimento: Aqui inclui a possibilidade de usar o H2
  • ? (8/13) Do you want to use Hibernate 2nd level cache? Yes, with ehcache (local cache, for a single node)
    Escolho se desejo usar o segundo nível de cache do Hibernate, temos 3 opções: Não, Sim com ehcache (cache local) ou com HazelCast (cache distribuído)
  • ? (9/13) Do you want to use clustered HTTP sessions? No
    Aqui seleciono se desejo usar cluster para as sessões com o HazelCast
  • ? (10/13) Do you want to use WebSockets? No
    Se quero usar WebSockets
  • ? (11/13) Would you like to use Maven or Gradle for building the backend? Maven (recommended)
    Escolho entre Maven ou Gradle
  • ? (12/13) Would you like to use Grunt or Gulp.js for building the frontend? Grunt (recommended)
    Semelhante a escolha acima, esta é para build de front-end.
  • ? (13/13) Would you like to use the Compass CSS Authoring Framework? No
    E a última pergunta é se queremos usar Compass

Após as escolhas, o JHipster irá baixar todos os arquivos necessários para nossa aplicação (este processo pode demorar alguns minutos). Para rodar a aplicação, simplesmente digitamos: mvn spring-boot:run

A aplicação estará disponível em http://localhost:8080

Página inicial

Página inicial

Monitoramento

Monitoramento

Gerenciadmento de logs

Gerenciadmento de logs

Internacionalização

O JHipster possui suporte a vários idiomas, por padrão vem instalado apenas inglês e francês. Mas para instalarmos português é simples, digitamos: yo jhipster:languages, procuramos pressionando a seta para baixo ou para cima e selecionamos com a tecla de espaço “Portuguese (Brazilian)”

Languages configuration is starting
? Please choose additional languages to install: (Press <space> to select)
❯◯ Catalan
 ◯ Chinese (Simplified)
 ◯ Chinese (Traditional)
 ◯ Danish
 ◯ German
 ◯ Korean
 ◯ Hungarian
(Move up and down to reveal more choices)

Entidades

Também podemos adicionar outras entitidades. Digamos que queremos criar uma entidade Autor e Livro, para isso precisariamos criar para cada entidade:

  • Uma tabela no banco
  • Uma JPA Entity
  • Um Spring Data JPA Repository
  • Um Controller com Spring MVC REST, com as operaçoes basicas de CRUD
  • Um AngularJS router, um controller e um service
  • Uma página HTML

Se necessitarmos criar relacionamentos, isso se torna ainda pior. Com o JHipster esta tarefa é muito fácil, digitamos no console yo jhipster:entity autor e respondemos algumas questões:

The entity autor is being created.
Generating field #1
? Do you want to add a field to your entity? Yes
? What is the name of your field? nome
? What is the type of your field? String
===========Autor==============
nome (String)
Generating field #2
? Do you want to add a field to your entity? Yes
? What is the name of your field? dataNascimento
? What is the type of your field? DateTime
===========Autor==============
nome (String)
dataNascimento (DateTime)
Generating field #3
? Do you want to add a field to your entity? No
===========Autor==============
nome (String)
dataNascimento (DateTime)
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? Livro
? What is the name of the relationship? livro
? What is the type of the relationship? one-to-many
===========Autor==============
nome (String)
dataNascimento (DateTime)
-------------------
livros - livro (one-to-many)
Generating relationships with other entities
? Do you want to add a relationship to another entity? No
===========Autor==============
nome (String)
dataNascimento (DateTime)
-------------------
livro - livro (one-to-many)
? Do you want pagination on your entity? Yes, with a simple pager
Everything is configured, generating the entity...
   create .jhipster/Autor.json
   create src/main/java/br/com/cadocruz/domain/Autor.java
   create src/main/java/br/com/cadocruz/repository/AutorRepository.java
   create src/main/java/br/com/cadocruz/web/rest/AutorResource.java
   create src/main/resources/config/liquibase/changelog/20150306164732_added_entity_Autor.xml
   create src/main/webapp/scripts/app/entities/autor/autors.html
   create src/main/webapp/scripts/app/entities/autor/autor-detail.html
   create src/main/webapp/scripts/app/entities/autor/autor.js
   create src/main/webapp/scripts/app/entities/autor/autor.controller.js
   create src/main/webapp/scripts/app/entities/autor/autor-detail.controller.js
   create src/main/webapp/scripts/components/entities/autor/autor.service.js
   create src/test/java/br/com/cadocruz/web/rest/AutorResourceTest.java
   create src/test/gatling/simulations/AutorGatlingTest.scala
   create src/main/webapp/i18n/en/autor.json
   create src/main/webapp/i18n/fr/autor.json
   create src/main/webapp/i18n/pt-br/autor.json

Repare que o JHipster já criou todos os arquivos necessários, nos poupando muito trabalho. Mas ainda precisamos criar a entidade Livro: yo jhipster:entity livro

The entity livro is being created.
Generating field #1
? Do you want to add a field to your entity? Yes
? What is the name of your field? titulo
? What is the type of your field? String
===========Livro==============
titulo (String)
Generating field #2
? Do you want to add a field to your entity? Yes
? What is the name of your field? dataPublicacao
? What is the type of your field? DateTime
===========Livro==============
titulo (String)
dataPublicacao (DateTime)
Generating field #3
? Do you want to add a field to your entity? No
===========Livro==============
titulo (String)
dataPublicacao (DateTime)
Generating relationships with other entities
? Do you want to add a relationship to another entity? Yes
? What is the name of the other entity? autor
? What is the name of the relationship? autor
? What is the type of the relationship? many-to-one
? When you display this relationship with AngularJS, which field from 'autor' do you want to use? nome
===========Livro==============
titulo (String)
dataPublicacao (DateTime)
-------------------
autor - autor (many-to-one)
Generating relationships with other entities
? Do you want to add a relationship to another entity? No
===========Livro==============
titulo (String)
dataPublicacao (DateTime)
-------------------
autor - autor (many-to-one)
? Do you want pagination on your entity? No
Everything is configured, generating the entity...
   create .jhipster/Livro.json
   create src/main/java/br/com/cadocruz/domain/Livro.java
   create src/main/java/br/com/cadocruz/repository/LivroRepository.java
   create src/main/java/br/com/cadocruz/web/rest/LivroResource.java
   create src/main/resources/config/liquibase/changelog/20150306170841_added_entity_Livro.xml
   create src/main/webapp/scripts/app/entities/livro/livros.html
   create src/main/webapp/scripts/app/entities/livro/livro-detail.html
   create src/main/webapp/scripts/app/entities/livro/livro.js
   create src/main/webapp/scripts/app/entities/livro/livro.controller.js
   create src/main/webapp/scripts/app/entities/livro/livro-detail.controller.js
   create src/main/webapp/scripts/components/entities/livro/livro.service.js
   create src/test/java/br/com/cadocruz/web/rest/LivroResourceTest.java
   create src/test/gatling/simulations/LivroGatlingTest.scala
   create src/main/webapp/i18n/en/livro.json
   create src/main/webapp/i18n/fr/livro.json
   create src/main/webapp/i18n/pt-br/livro.json

Subindo novamente a aplicação podemos ver que nossas entidades foram criadas com as operações básicas de CRUD.

Entidades

Autor

Livro

Services

Caso queira criar Services (para sua regra de negócio) o JHipster também tem um comando específico para isso: yo jhipster:service [nome da service].

Conclusão

Apesar de ser um projeto novo, o JHipster possui várias funcionalidades interessantes que facilitam a criação de Web Apps usando Spring Boot e AngularJS.

Enquanto eu escrevia este post, saiu a nova versão (2.5.2) do JHipster, com várias correções de bugs, confira o Release notes

Segurança com autenticação Digest no Spring Security em serviços RESTful

Autenticação Digest

Para utilizarmos a atenticação Digest devemos saber como ele funciona. Ao contrário da autenticação Basic, a autenticação Digest não envia a senha em texto puro pelo meio. A Autenticação Digest é um mecanismo de autenticação simples desenvolvido originalmente para o protocolo HTTP que está descrito na RFC2671.

O cliente envia um requisição à um conteúdo, que requer autenticação, sem fornecer usuário e senha. Ex.:

GET /api/hello HTTP/1.1
Host: localhost:8080

Neste caso acessamos o conteúdo (path) /api/hello pelo método GET. O servidor responde com 401 Unauthorized e gera um desafio digest e o envia para o cliente.

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
WWW-Authenticate: Digest realm="MyRealm", qop="auth", nonce="MTQyNDY1MDM4MTY2MDo3YmQ4NjFlYmQwNWNhYzZmMjE3YTBjYmUzNzc0YjdlMg=="
Content-Type: application/json;charset=UTF-8
Date: Sun, 22 Feb 2015 23:53:01 GMT

Não tenho a pretensão de ser um guia definitivo sobre autenticação Digest, por isso não vou explicar cada parâmetro enviado no HEADER. Esses parâmetros são usados na resposta ao desafio. (Para mais informações sobre os parâmetros acessar o site RFC2671 ).
O nonce é uma string gerada pelo servidor toda vez que lança um desafio. Deve ser única, ou seja, não deve ser repetido. No Spring Security o nonce é gerado da seguinte maneira:

long expiryTime = System.currentTimeMillis() + (nonceValiditySeconds * 1000);
String signatureValue = DigestAuthUtils.md5Hex(expiryTime + ":" + key);
String nonceValue = expiryTime + ":" + signatureValue;
String nonceValueBase64 = new String(Base64.encode(nonceValue.getBytes()));

Com base no nonce devemos enviar a resposta pelo HEADER do HTTP:

Authorization: Digest username="cadocruz", realm="MyRealm", nonce="MTQyNDY1MDM4MTY2MDo3YmQ4NjFlYmQwNWNhYzZmMjE3YTBjYmUzNzc0YjdlMg==", uri="/api/hello", response="94785d1c6feae05bbc8e8d47590224a7", qop=auth, nc=00000001, cnonce="0a4f113b"

Note o paramêtro response, é neste parâmetro que devemos passar a resposta pelo HEADER, o response é calculado da seguinte maneira:

A1 = MD5(username ":" realm ":" passwd)
A2 = MD5(method ":" uri)
response = MD5(A1:nonce:nc:cnonce:qop:A2)

Com base nesses dados vamos à nossa aplicação.

Spring Boot

O Spring Boot é um projeto com o conceito de “convenção sobre configuração”, tornando a configuração do ambiente muito mais rápida e simples. Você pode aprender um pouco mais sobre o Spring Boot na página de referência do próprio Spring Boot. Um jeito simples de criar um projeto do Spring Boot é pelo site Spring Initializr, é só você configurar como será sua aplicação e as depêndencias dela, e gerar o projeto. Estou usando o Gradle ao invés do Maven, mas você pode usar o maven se quiser. O arquivo build.gradle da nossa aplicação ficou assim:

buildscript {
    ext {
        springBootVersion = '1.2.1.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse-wtp'
apply plugin: 'spring-boot' 
apply plugin: 'war'

war {
    baseName = 'spring-security-digest'
    version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
targetCompatibility = 1.8

repositories {
    mavenCentral()
}

configurations {
    providedRuntime
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-security")
    compile("org.springframework.boot:spring-boot-starter-web")
    compile("org.springframework.boot:spring-boot-starter-data-rest")
    providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
    testCompile("org.springframework.boot:spring-boot-starter-test")
}

eclipse {
    classpath {
         containers.remove('org.eclipse.jdt.launching.JRE_CONTAINER')
         containers 'org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8'
    }
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.12'
}

O coração do Spring Boot é nosso SpringSecurityDigestApplication.

@SpringBootApplication
@ComponentScan("br.com.cadocruz")
public class SpringSecurityDigestApplication {
	
    public static void main(String[] args) {
        SpringApplication.run(SpringSecurityDigestApplication.class, args);
    }
}

Pronto, já temos nossa aplicação rodando com o mínimo de segurança.

Spring Security

Diferentemente da autenticação Basic, na autenticação Digest o Spring Security não provê uma configuração “automática”, então teremos que definir manualmente nossos DigestAuthenticationEntryPoint e DigestAuthenticationFilter.

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

	protected void configure(HttpSecurity http) throws Exception {
		http
			.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
			.authorizeRequests()
				.antMatchers("/api/**").authenticated()
			.and().exceptionHandling()
			.authenticationEntryPoint(digestEntryPoint());
		http.csrf().disable();
		http.addFilter(digestAuthenticationFilter(digestEntryPoint()));
	}

	protected void configure(AuthenticationManagerBuilder auth)
			throws Exception {
		auth.inMemoryAuthentication().withUser("cadocruz").password("12345678")
				.authorities("ROLE_USER").and().withUser("admin")
				.password("12345678").authorities("ROLE_USER", "ROLE_ADMIN");
	}
	
	@Bean
	public DigestAuthenticationEntryPoint digestEntryPoint() {
		DigestAuthenticationEntryPoint digestAuthenticationEntryPoint = new DigestAuthenticationEntryPoint();
		digestAuthenticationEntryPoint.setKey("mykey");
		digestAuthenticationEntryPoint.setNonceValiditySeconds(120);
		digestAuthenticationEntryPoint.setRealmName("MyRealm");
		return digestAuthenticationEntryPoint;
	}

	public DigestAuthenticationFilter digestAuthenticationFilter(
			DigestAuthenticationEntryPoint digestAuthenticationEntryPoint)
			throws Exception {
		DigestAuthenticationFilter digestAuthenticationFilter = new DigestAuthenticationFilter();
		digestAuthenticationFilter
				.setAuthenticationEntryPoint(digestEntryPoint());
		digestAuthenticationFilter
				.setUserDetailsService(userDetailsServiceBean());
		return digestAuthenticationFilter;
	}

	@Override
	@Bean
	public UserDetailsService userDetailsServiceBean() throws Exception {
		return super.userDetailsServiceBean();
	}

}
Algumas considerações:
Toda requisição para /api deve ser autenticada
Desabilitamos o HTTP Session.
Desabilitamos a proteção contra CSRF (Cross-site Request Forgery) para simplificar o desenvolvimento, mas você pode ler mais sobre proteção CSRF aqui.

No método digestEntryPoint() configuramos nossa chave privada, o tempo de expiração do nonce em segundos (o valor padrão é 300 segundos) e o nosso realm. Esses são parâmetros obrigatórios.

Vamos agora para nosso @RestController, ele não tem nada de mais. Mapeamos nosso controller para /api e nosso método hello como /hello.

@RestController
@RequestMapping("/api")
public class HelloController {

	@RequestMapping(value = "/hello", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
	public ResponseEntity<HelloMessage> hello(Principal principal) {
		return new ResponseEntity<HelloMessage>(new HelloMessage("Hello, "
				+ principal.getName() + "!"), HttpStatus.OK);
	}

	class HelloMessage {
		private String message;

		public HelloMessage(String message) {
			this.message = message;
		}

		public String getMessage() {
			return message;
		}
	}
}

Vamos testar nossa aplicação usando o cURL, sem providenciar as credenciais de segurança:

curl -i http://localhost:8080/api/hello

Como esperado, recebemos como resposta o código de status 401 Unauthorized.

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
WWW-Authenticate: Digest realm="MyRealm", qop="auth", nonce="MTQyNDc0MTA5NDYzNzpiMGRhMjZlNzgxNWEyYWIwOGQ1ZDllMWVhYTg3NDU1Ng=="
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 24 Feb 2015 01:04:54 GMT

{"timestamp":1424739894638,"status":401,"error":"Unauthorized","message":"Full authentication is required to access this resource","path":"/api/hello"}

Agora vamos passar o usuário e senha para nossa aplicação:

curl -i --digest --user cadocruz:12345678 http://localhost:8080/api/hello

A primeira resposta do servidor ainda é 401 Unauthorized, mas o desafio agora é interpretado e enviado em um segundo request, que irá ter sucesso com 200 OK:

HTTP/1.1 401 Unauthorized
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
WWW-Authenticate: Digest realm="MyRealm", qop="auth", nonce="MTQyNDc0MTcxMzA3NDpiMWVkOTU3YWMxNGYzMGYwYzljMDVmZDA2ZDA2MDI1Zg=="
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 24 Feb 2015 01:15:13 GMT

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 24 Feb 2015 01:15:13 GMT

{"message":"Hello, cadocruz!"}

O cliente pode enviar o Authorization header já no primeiro request, e evitar o desafio de segurança do segundo request.

Agora que já temos nosso serviço RESTful funcionando, vamos criar nossa página de login (index.html) que consumirá nosso serviço usando a autenticação Digest.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Login Page</title>
  </head>
  <body>
    <div>
       <fieldset>
           <legend>Please Login</legend>
           <label for="username">Username</label>
           <input type="text" id="username" name="username"/>
           <label for="password">Password</label>
           <input type="password" id="password" name="password"/>
           <div class="form-actions">
               <button type="button" id="btLogin">Log in</button>
           </div>
       </fieldset>
    </div>
  </body>
</html>

Nada de mais na nossa página, um campo para usuário e outro para senha. O serviço “sujo” deve ser feito por um javascript fazendo requisição por AJAX. Nós mesmo podemos criar nosso javascript, mas com certeza alguém já fez. Pesquisando “ajax digest auth” no google, o terceiro resultado é do Marcin Michalski ‘s Weblog, pegarei este como exemplo:

/*
 * A JavaScript implementation of the Digest Authentication
 * Digest Authentication, as defined in RFC 2617.
 * Version 1.0 Copyright (C) Maricn Michalski (https://marcin-michalski.pl) 
 * Distributed under the BSD License
 * 
 * site: https://arrowgroup.eu
 */
 
$.Class("pl.arrowgroup.DigestAuthentication", {
 MAX_ATTEMPTS : 1,
 AUTHORIZATION_HEADER : "Authorization",
 WWW_AUTHENTICATE_HEADER : 'WWW-Authenticate',
 NC : "00000001", //currently nc value is fixed it is not incremented
 HTTP_METHOD : "GET",
 /**
  * settings json:
  *  - onSuccess - on success callback
  *  - onFailure - on failure callback
  *  - username - user name
  *  - password - user password
  *  - cnonce - client nonce
  */
 init : function(settings) {
   this.settings = settings;
 },
 setCredentials: function(username, password){
   this.settings.username = username;
   this.settings.password = password;
 },
 call : function(uri){
   this.attempts = 0;
   this.invokeCall(uri);
 },
 invokeCall: function(uri,authorizationHeader){
   var digestAuth = this;
   $.ajax({
         url: uri,
         type: this.HTTP_METHOD,
         beforeSend: function(request){
          if(typeof authorizationHeader != 'undefined'){
          request.setRequestHeader(digestAuth.AUTHORIZATION_HEADER, authorizationHeader);           
          }
         },
         success: function(response) {
          digestAuth.settings.onSuccess(response);          
         },
         error: function(response) { 
          if(digestAuth.attempts == digestAuth.MAX_ATTEMPTS){
      digestAuth.settings.onFailure(response);
      return;
      }
          var paramParser = new pl.arrowgroup.HeaderParamsParser(response.getResponseHeader(digestAuth.WWW_AUTHENTICATE_HEADER));
          var nonce = paramParser.getParam("nonce");
          var realm = paramParser.getParam("realm");
          var qop = paramParser.getParam("qop");
          var response = digestAuth.calculateResponse(uri, nonce, realm, qop);
          var authorizationHeaderValue = digestAuth.generateAuthorizationHeader(paramParser.headerValue, response, uri); 
          digestAuth.attempts++;
          digestAuth.invokeCall(uri, authorizationHeaderValue);
         }             
     });
 },
 calculateResponse : function(uri, nonce, realm, qop){
   var a2 = this.HTTP_METHOD + ":" + uri;
   var a2Md5 = hex_md5(a2);
   var a1Md5 = hex_md5(this.settings.username + ":" + realm + ":" + this.settings.password);
   var digest = a1Md5 + ":" + nonce + ":" + this.NC + ":" + this.settings.cnonce + ":" + qop + ":" +a2Md5;
   return hex_md5(digest);
 },
 generateAuthorizationHeader : function(wwwAuthenticationHeader, response, uri){
   return wwwAuthenticationHeader+', username="'+this.settings.username+'", uri="'+
      uri+'", response="'+response+'", nc='+
      this.NC+', cnonce="'+this.settings.cnonce+'"';
   }
});
$.Class("pl.arrowgroup.HeaderParamsParser",{
 init : function(headerValue) {
   this.headerValue = headerValue;
   this.headerParams = this.headerValue.split(",");
 },
 getParam: function(paramName){
   var paramVal = null;
   $.each(this.headerParams, function(index, value){
     if(value.indexOf(paramName)>0){
     paramVal = value.split(paramName+"=")[1];
     paramVal = paramVal.substring(1, paramVal.length-1);
     }
   });
   return paramVal;
 }
});

Ele tem como dependências o JQuery, JQuery.Class e MD5.

Vamos ver como ficou nosso index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Login Page</title>
  </head>
  <body>
    <div>
       <fieldset>
           <legend>Please Login</legend>
           <label for="username">Username</label>
           <input type="text" id="username" name="username"/>
           <label for="password">Password</label>
           <input type="password" id="password" name="password"/>
           <div class="form-actions">
               <button type="button" id="btLogin">Log in</button>
           </div>
       </fieldset>
    </div>
    <div id="response">
    </div>
    <script src="//code.jquery.com/jquery-1.11.2.min.js"></script>
    <script type="text/javascript" src="jquery.class.min.js"></script>
    <script type="text/javascript" src="md5-min.js"></script>
    <script type="text/javascript" src="digest-auth.js"></script>
    <script type="text/javascript" src="login.js"></script>
  </body>
</html>

nosso login.js ficou assim:

jQuery(document).ready(function($) {
	var digestAuth = new pl.arrowgroup.DigestAuthentication({
		onSuccess : function(response) {
			console.log("Success " + response.message);
			$("#response").html(response.message);
		},
		onFailure : function(response) {
			console.log("Failure " + response.responseJSON.message);
			$("#response").html('Invalid credentials !!! ' + response.responseJSON.message);
		},
		cnonce : 'testCnonce'
	});
	
	$('#btLogin').click(function () {
		digestAuth.setCredentials($('#username').val(), $('#password').val());
		digestAuth.call('/api/hello');
	});
});

Para rodarmos a aplicação, simplesmente digitamos gradle clean build run no console. Vai aparecer algo semelhante com a saída abaixo:

:clean
:compileJava
:processResources
:classes
:war
:bootRepackage
:assemble
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
2015-02-24 18:55:49.996  INFO 10137 --- [       Thread-4] o.s.w.c.s.GenericWebApplicationContext   : Closing org.springframework.web.context.support.GenericWebApplicationContext@574804e7: startup date [Tue Feb 24 18:55:45 BRT 2015]; root of context hierarchy
:check
:build
:findMainClass
:run

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.1.RELEASE)

2015-02-24 18:55:50.952  INFO 10138 --- [           main] b.c.c.c.SpringSecurityDigestApplication  : Starting SpringSecurityDigestApplication on new-host-3.home with PID 10138 

Testando nossa aplicação podemos perceber que recebemos a caixa de diálogo do login, pedindo usuário e senha. Como na imagem abaixo:

Login Dialog

Como contornar isso? No mesmo post do Marcin Michalski ‘s Weblog ele da uma solução para isso não ocorrer. Estender a classe DigestAuthenticationEntryPoint, o que ele faz é alterar o código de status 401 Unauthorized para 403 Forbidden.

 
public class AjaxDigestAuthenticationEntryPoint extends DigestAuthenticationEntryPoint{
 
  @Override
  public void commence(HttpServletRequest request, HttpServletResponse response, 
             AuthenticationException authException) throws IOException, ServletException {
    super.commence(request, new UnauthorizedHttpResponse(response), authException);
  }
 
  private static class UnauthorizedHttpResponse extends HttpServletResponseWrapper{
    public UnauthorizedHttpResponse(HttpServletResponse response) {
      super(response);
    }
    @Override
    public void sendError(int sc, String msg) throws IOException {
      if(sc == HttpServletResponse.SC_UNAUTHORIZED){
        sc = HttpServletResponse.SC_FORBIDDEN;
      }
      super.sendError(sc, msg);
    }
  }
}

Após muita pesquisa para descobrir a melhor forma de contornar este problema, cheguei a uma solução, alterar o HTTP Authentication Scheme. Quando enviamos o HEADER para o cliente, enviamos o tipo de autenticação (Authentication Scheme) WWW-Authenticate: Digest, para que o browser não mostre a caixa de diálogo do login, criaremos nosso próprio Authentication Scheme.

O que precisamos fazer é estender a classe DigestAuthenticationEntryPoint e criarmos nossa própria implementação do Digest. Na verdade usaremos o mesmo código do DigestAuthenticationEntryPoint, mudando apenas o Authentication Scheme de Digest para outro de nossa escolha, usarei DigestCustom.

Dei o nome da classe de RestDigestAuthenticationEntryPoint, veja abaixo como ficou o código:

public class RestDigestAuthenticationEntryPoint extends
		DigestAuthenticationEntryPoint {

	private static final Log logger = LogFactory
			.getLog(RestDigestAuthenticationEntryPoint.class);

	public void commence(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException authException)
			throws IOException, ServletException {
		HttpServletResponse httpResponse = (HttpServletResponse) response;

		// compute a nonce (do not use remote IP address due to proxy farms)
		// format of nonce is:
		// base64(expirationTime + ":" + md5Hex(expirationTime + ":" + key))
		long expiryTime = System.currentTimeMillis()
				+ (super.getNonceValiditySeconds() * 1000);
		String signatureValue = DigestUtils.md5DigestAsHex(new String(
				expiryTime + ":" + super.getKey()).getBytes());
		String nonceValue = expiryTime + ":" + signatureValue;
		String nonceValueBase64 = new String(Base64.encode(nonceValue
				.getBytes()));

		// qop is quality of protection, as defined by RFC 2617.
		// we do not use opaque due to IE violation of RFC 2617 in not
		// representing opaque on subsequent requests in same session.
		String authenticateHeader = "DigestCustom realm=\"" + super.getRealmName()
				+ "\", " + "qop=\"auth\", nonce=\"" + nonceValueBase64 + "\"";

		if (authException instanceof NonceExpiredException) {
			authenticateHeader = authenticateHeader + ", stale=\"true\"";
		}

		if (logger.isDebugEnabled()) {
			logger.debug("WWW-Authenticate header sent to user agent: "
					+ authenticateHeader);
		}

		httpResponse.addHeader("WWW-Authenticate", authenticateHeader);
		httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED,
				authException.getMessage());
	}
}

Devemos mudar nossa classe SecurityConfig e trocar a classe DigestAuthenticationEntryPoint pela RestDigestAuthenticationEntryPoint

	@Bean
	public RestDigestAuthenticationEntryPoint digestEntryPoint() {
		RestDigestAuthenticationEntryPoint digestAuthenticationEntryPoint = new RestDigestAuthenticationEntryPoint();
		digestAuthenticationEntryPoint.setKey("mykey");
		digestAuthenticationEntryPoint.setNonceValiditySeconds(120);
		digestAuthenticationEntryPoint.setRealmName("MyRealm");
		return digestAuthenticationEntryPoint;
	}

Também devemos mudar o javascript digest-auth.js, pois o Spring Security não aceitará nosso DigestCustom sem termos que implementar nosso próprio filter, mas não queremos isso, devemos enviar no HEADER, Authorization: Digest.

 generateAuthorizationHeader : function(wwwAuthenticationHeader, response, uri){
   wwwAuthenticationHeader = wwwAuthenticationHeader.replace("DigestCustom", "Digest");
   return wwwAuthenticationHeader+', username="'+this.settings.username+'", uri="'+
      uri+'", response="'+response+'", nc='+
      this.NC+', cnonce="'+this.settings.cnonce+'"';
   }

Pronto, agora estamos usando todo o poder do Spring Security, com autenticação Digest, nos nossos serviços RESTful.

Você pode fazer o download do código fonte aqui

Fontes:
RFC2671
Spring Security
Digest access authentication