Comment JWT fonctionne réellement avec Spring MVC pour créer un jeton et valider jeton?
Je suis actuellement à la recherche pour obtenir plus de compréhension sur la JWT
concept et comment il fonctionne avec Spring MVC
. J'ai trouvé le lien https://github.com/nielsutrecht/jwt-angular-spring, dont le programme fonctionne très bien. Mais je me demande comment il crée un jeton puis comment l'utilisateur se connecter à l'application. J'ai besoin de quelques éclaircissements /réponse comment ça marche? S'il vous plaît ne nécessaire.
Ma compréhension: Il me semble que lorsque vous lancez l'application, GenericFilterBean mise en œuvre de la classe sera appelé, ce qui va générer le JWT jeton et l'envoyer à l'INTERFACE utilisateur dans le stockage local (pas sûr), et puis ce jeton viendra en-tête de la demande et elle sera validée et l'accès sera donné à l'utilisateur ?
Je voudrais mettre quelques extrait de code de référence (même si vous pouvez regarder pour le code de lien mentionné)
AuthenticationTokenProcessingFilter.java
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
private final UserDetailsService userService;
public AuthenticationTokenProcessingFilter(UserDetailsService userService){
this.userService = userService;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,ServletException{
HttpServletRequest httpRequest = this.getAsHttpRequest(request);
String authToken = this.extractAuthTokenFromRequest(httpRequest);
String userName = TokenUtils.getUserNameFromToken(authToken);
if (userName != null) {
UserDetails userDetails = this.userService.loadUserByUsername(userName);
if (TokenUtils.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
private HttpServletRequest getAsHttpRequest(ServletRequest request){
if (!(request instanceof HttpServletRequest)) {
throw new RuntimeException("Expecting an HTTP request");
}
return (HttpServletRequest) request;
}
private String extractAuthTokenFromRequest(HttpServletRequest httpRequest){
/* Get token from header */
String authToken = httpRequest.getHeader("X-Auth-Token");
System.out.println("AUTH TOKEN : "+authToken);
/* If token not found get it from request parameter */
if (authToken == null) {
authToken = httpRequest.getParameter("token");
}
return authToken;
}
}
TokenUtils.java
public class TokenUtils{
public static final String MAGIC_KEY = "obfuscate";
public static String createToken(UserDetails userDetails){
System.out.println(" ----- Create Token ------");
/* Expires in one hour */
long expires = System.currentTimeMillis() + 1000L * 60 * 60;
StringBuilder tokenBuilder = new StringBuilder();
tokenBuilder.append(userDetails.getUsername());
tokenBuilder.append(":");
tokenBuilder.append(expires);
tokenBuilder.append(":");
tokenBuilder.append(TokenUtils.computeSignature(userDetails, expires));
return tokenBuilder.toString();
}
public static String computeSignature(UserDetails userDetails, long expires){
System.out.println("------ Compute Signature ------");
StringBuilder signatureBuilder = new StringBuilder();
signatureBuilder.append(userDetails.getUsername());
signatureBuilder.append(":");
signatureBuilder.append(expires);
signatureBuilder.append(":");
signatureBuilder.append(userDetails.getPassword());
signatureBuilder.append(":");
signatureBuilder.append(TokenUtils.MAGIC_KEY);
MessageDigest digest;
try {
digest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("No MD5 algorithm available!");
}
System.out.println(new String(Hex.encode(digest.digest(signatureBuilder.toString().getBytes()))));
return new String(Hex.encode(digest.digest(signatureBuilder.toString().getBytes())));
}
public static String getUserNameFromToken(String authToken){
System.out.println("----- Get Username From TOken ----");
if (null == authToken) {
return null;
}
String[] parts = authToken.split(":");
return parts[0];
}
public static boolean validateToken(String authToken, UserDetails userDetails) {
System.out.println("=== Validate Token ===");
String[] parts = authToken.split(":");
long expires = Long.parseLong(parts[1]);
String signature = parts[2];
if (expires < System.currentTimeMillis()) {
return false;
}
System.out.println(signature.equals(TokenUtils.computeSignature(userDetails, expires)));
return signature.equals(TokenUtils.computeSignature(userDetails, expires));
}
}
Context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd">
<context:annotation-config />
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:database.properties</value>
</list>
</property>
</bean>
<!-- MySQL DataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="username" value="${jdbc.user}" />
<property name="password" value="${jdbc.pass}" />
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
</bean>
<!-- Entity Manager Factory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="examplePU" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true" />
<property name="showSql" value="true" />
</bean>
</property>
</bean>
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="newsEntryDao" class="net.dontdrinkandroot.dao.newsentry.JpaNewsEntryDao" />
<bean id="userDao" class="net.dontdrinkandroot.dao.user.JpaUserDao" />
<bean id="dataBaseInitializer" class="net.dontdrinkandroot.dao.DataBaseInitializer" init-method="initDataBase">
<constructor-arg ref="userDao" />
<constructor-arg ref="newsEntryDao" />
<constructor-arg ref="passwordEncoder" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- =======================
INIT REST COMPONENTS
======================= -->
<context:component-scan base-package="net.dontdrinkandroot.resources" />
<bean id="objectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
<!-- ======================================
SPRING SECURITY SETUP
====================================== -->
<bean id="passwordEncoder" class="org.springframework.security.crypto.password.StandardPasswordEncoder">
<constructor-arg value="ThisIsASecretSoChangeMe" />
</bean>
<security:authentication-manager id="authenticationManager">
<security:authentication-provider user-service-ref="userDao">
<security:password-encoder ref="passwordEncoder"></security:password-encoder>
</security:authentication-provider>
</security:authentication-manager>
<security:http
realm="Protected API"
use-expressions="true"
auto-config="false"
create-session="stateless"
entry-point-ref="unauthorizedEntryPoint"
authentication-manager-ref="authenticationManager">
<security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
<security:intercept-url pattern="/rest/user/authenticate" access="permitAll" />
<security:intercept-url method="GET" pattern="/rest/news/**" access="hasRole('user')" />
<security:intercept-url method="PUT" pattern="/rest/news/**" access="hasRole('admin')" />
<security:intercept-url method="POST" pattern="/rest/news/**" access="hasRole('admin')" />
<security:intercept-url method="DELETE" pattern="/rest/news/**" access="hasRole('admin')" />
</security:http>
<bean id="unauthorizedEntryPoint" class="net.dontdrinkandroot.rest.UnauthorizedEntryPoint" />
<bean id="authenticationTokenProcessingFilter" class="net.dontdrinkandroot.rest.AuthenticationTokenProcessingFilter" >
<constructor-arg ref="userDao" />
</bean>
</beans>
EDIT-1:
AUTH TOKEN : null
----- Get Username From Token ----
Jan 01, 2016 2:35:01 AM com.sun.jersey.spi.container.servlet.WebComponent filterFormParameters
WARNING: A servlet request, to the URI http://localhost:8080/angular-rest-security/rest/user/authenticate, contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using @FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
Hibernate: select user0_.id as id1_1_, user0_.name as name2_1_, user0_.password as password3_1_ from User user0_ where user0_.name=?
Hibernate: select roles0_.User_id as User_id1_1_0_, roles0_.roles as roles2_2_0_ from User_roles roles0_ where roles0_.User_id=?
Hibernate: select user0_.id as id1_1_, user0_.name as name2_1_, user0_.password as password3_1_ from User user0_ where user0_.name=?
Hibernate: select roles0_.User_id as User_id1_1_0_, roles0_.roles as roles2_2_0_ from User_roles roles0_ where roles0_.User_id=?
----- Create Token ------
------ Compute Signature ------
525b8e635bb234684d2a02b99f38d687
AUTH TOKEN : null
----- Get Username From Token ----
Jan 01, 2016 2:36:27 AM com.sun.jersey.spi.container.servlet.WebComponent filterFormParameters
WARNING: A servlet request, to the URI http://localhost:8080/angular-rest-security/rest/user/authenticate, contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using @FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
AUTH TOKEN : admin:1451599569652:525b8e635bb234684d2a02b99f38d687
----- Get Username From Token ----
Hibernate: select user0_.id as id1_1_, user0_.name as name2_1_, user0_.password as password3_1_ from User user0_ where user0_.name=?
Hibernate: select roles0_.User_id as User_id1_1_0_, roles0_.roles as roles2_2_0_ from User_roles roles0_ where roles0_.User_id=?
Hibernate: select user0_.id as id1_1_, user0_.name as name2_1_, user0_.password as password3_1_ from User user0_ where user0_.name=?
Hibernate: select roles0_.User_id as User_id1_1_0_, roles0_.roles as roles2_2_0_ from User_roles roles0_ where roles0_.User_id=?
=== Validate Token ===
------ Compute Signature ------
525b8e635bb234684d2a02b99f38d687
true
------ Compute Signature ------
525b8e635bb234684d2a02b99f38d687
Hibernate: select user0_.id as id1_1_, user0_.name as name2_1_, user0_.password as password3_1_ from User user0_ where user0_.name=?
Hibernate: select roles0_.User_id as User_id1_1_0_, roles0_.roles as roles2_2_0_ from User_roles roles0_ where roles0_.User_id=?
----- Create Token ------
------ Compute Signature ------
b6238344022f3f4dd3787f0f8fa99b44
AUTH TOKEN : null
----- Get Username From Token ----
AUTH TOKEN : admin:1451599596826:b6238344022f3f4dd3787f0f8fa99b44
----- Get Username From Token ----
Hibernate: select user0_.id as id1_1_, user0_.name as name2_1_, user0_.password as password3_1_ from User user0_ where user0_.name=?
Hibernate: select roles0_.User_id as User_id1_1_0_, roles0_.roles as roles2_2_0_ from User_roles roles0_ where roles0_.User_id=?
=== Validate Token ===
------ Compute Signature ------
b6238344022f3f4dd3787f0f8fa99b44
true
------ Compute Signature ------
b6238344022f3f4dd3787f0f8fa99b44
AUTH TOKEN : admin:1451599596826:b6238344022f3f4dd3787f0f8fa99b44
----- Get Username From Token ----
Hibernate: select user0_.id as id1_1_, user0_.name as name2_1_, user0_.password as password3_1_ from User user0_ where user0_.name=?
Hibernate: select roles0_.User_id as User_id1_1_0_, roles0_.roles as roles2_2_0_ from User_roles roles0_ where roles0_.User_id=?
=== Validate Token ===
------ Compute Signature ------
b6238344022f3f4dd3787f0f8fa99b44
true
------ Compute Signature ------
b6238344022f3f4dd3787f0f8fa99b44
02:36:38,728 INFO NewsEntryResource.list():49 - list()
Hibernate: select newsentry0_.id as id1_0_, newsentry0_.content as content2_0_, newsentry0_.date as date3_0_ from NewsEntry newsentry0_ order by newsentry0_.date desc
AUTH TOKEN : null
----- Get Username From Token ----
OriginalL'auteur | 2015-12-31
Vous devez vous connecter pour publier un commentaire.
Je suis l'auteur du lien (https://github.com/nielsutrecht/jwt-angular-spring) vous a donné.
De l'exemple d'application utilise le Jjwt bibliothèque pour créer et déchiffrer le JSON web jetons. Dans mon exemple d'application le jeton est créé lorsqu'une personne parvient à se connecter avec succès. Ce qui se passe dans le
login()
méthode dansUserController.java
. Une connexion réussie (l'exemple de l'application n'a pas affaire à silly stuff comme les mots de passe pour des raisons de simplicité) renvoie unLoginResponse
avec ce jeton. L'angle d'application qui définit cela comme un défaut d'en-tête qui est envoyé avec chaque demande. Vous pouvez stocker ce dans le local de stockage ou d'un cookie si vous ne voulez pas pour les avoir à vous connecter à nouveau après l'appui sur la touche F5. De nouveau; c'est un exemple qui maintient les choses aussi simples que possible; j'ai quitté ces sortes de choses sur le but.L'en-tête est lu par le
JwtFilter
classe et stockées dans le contexte de la demande. De cette façon, n'importe quel chemin peut accéder à ces informations sans avoir à déchiffrer l'en-tête lui-même.Bien que tout est expliqué assez en profondeur dans le post de blog: laissez-moi savoir si il ya quelque chose que vous ne comprenez pas.
OriginalL'auteur Niels Dommerholt
Lorsque vous effectuez la connexion, vous envoyer le nom d'utilisateur et mot de passe via requête HTTP post. Java web service répond avec jeton si les informations d'identification sont correctes. Frontend AngularJS les banques de l'application de jeton dans une LocalStorage ou un Cookie. Lors de chaque demande que vous faites, jeton est envoyé en tant que valeur de
Authorization
en-tête. Java web service intercepter les demandes et à vérifier l'existence et la validité du jeton.Vous pouvez prendre un coup d'oeil à ce démo de l'application j'ai fait trop.
Je vous remercie. J'ai vider le cache et je ne vois pas de jeton de maintenant avant la connexion. Je vais supprimer MODIFIER-1.
Vous êtes les bienvenus. 🙂 D'envisager d'accepter de répondre si cela vous a aidé.
Oui, va le faire, mais j'ai une dernière question à poser. Il apparaît que la mise en œuvre de la classe de GenericFilterBean est appelé à de nombreuses reprises et également loadByUsername. Savez-vous pourquoi? (Pls mise à jour de votre réponse trop), j'ai aussi créer MODIFIER-1 pour montrer certains journaux comme une preuve.
Pas sûr d'être honnête. Semble que tout va bien avec application à partir du lien que vous avez posté. Se sentir libre de s'appuyer sur le code de mon application démo.
OriginalL'auteur Branislav Lazic