java JWT 实际上如何与 Spring MVC 一起创建令牌和验证令牌?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34551390/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How JWT actually works with Spring MVC to create token and validate token?
提问by
I am actually looking to get more and clear understanding over the JWT
concept and how it works with Spring MVC
. I found link https://github.com/nielsutrecht/jwt-angular-spring, whose program works absolutely fine. But I wonder how it creates token and then how user gets login into the application.I need some clarification / answer how it works? Please do needful.
我实际上希望对这个JWT
概念以及它如何与Spring MVC
. 我找到了链接https://github.com/nielsutrecht/jwt-angular-spring,其程序运行良好。但我想知道它如何创建令牌,然后用户如何登录到应用程序。我需要一些说明/回答它是如何工作的?请做有需要的。
My understanding: It appears to me that when you launch the application, GenericFilterBean implementation class will be called which will generate the JWT token and send it to UI in local storage (not sure though) and then this token will come in header with request and then it will get validated and access will be given to user ?
我的理解:在我看来,当您启动应用程序时,将调用 GenericFilterBean 实现类,该类将生成 JWT 令牌并将其发送到本地存储中的 UI(虽然不确定),然后该令牌将与请求和那么它将得到验证并授予用户访问权限?
I would like to put some code snippet for reference (even you can look for code from link mentioned)
我想放一些代码片段以供参考(即使您可以从提到的链接中查找代码)
AuthenticationTokenProcessingFilter.java
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
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
<?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.Hymanson.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:
编辑-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 ----
回答by Niels Dommerholt
I'm the author of the link (https://github.com/nielsutrecht/jwt-angular-spring) you gave.
我是您提供的链接 ( https://github.com/nielsutrecht/jwt-angular-spring)的作者。
The example application uses the Jjwt library to create and decrypt the JSON web tokens. In my example application the token is created when a person manages to log in successfully. This happens in the login()
method in UserController.java
. A successful login (the example application doesn't deal with silly stuff like passwords for simplicity) returns a LoginResponse
with this token. The angular application than sets this as a default header that gets sent with every request. You can store this in local storage or a cookie if you don't want to have them log in again after hitting F5. Again; it's an example that keeps stuff as simple as possible; I left these kinds of things out on purpose.
示例应用程序使用 Jjwt 库来创建和解密 JSON Web 令牌。在我的示例应用程序中,当一个人成功登录时会创建令牌。这发生在 中的login()
方法中UserController.java
。成功登录(示例应用程序为了简单起见不处理诸如密码之类的愚蠢内容)返回LoginResponse
带有此令牌的 。Angular 应用程序将其设置为随每个请求一起发送的默认标头。如果您不想让他们在按 F5 后再次登录,您可以将其存储在本地存储或 cookie 中。再次; 这是一个让事情尽可能简单的例子;我故意忽略了这些东西。
The header gets read by the JwtFilter
class and stored in the request context. That way any path can access this information without having to decrypt the header itself.
标头由JwtFilter
类读取并存储在请求上下文中。这样,任何路径都可以访问此信息,而无需解密标头本身。
Although everything is explained pretty in-depth in the corresponding blog post: let me know if there's anything you don't understand.
尽管在相应的博客文章中对所有内容进行了非常深入的解释:如果您有任何不明白的地方,请告诉我。
回答by Branislav Lazic
When you perform login, you send username and password via HTTP post request. Java web service responds with token if credentials are correct. Frontend AngularJS application stores token into a LocalStorage or as a Cookie. Upon each next request you make, token is sent as a value of Authorization
header. Java web service will intercept requests and check for existence and validity of token.
当您执行登录时,您通过 HTTP post 请求发送用户名和密码。如果凭据正确,Java Web 服务会使用令牌进行响应。前端 AngularJS 应用程序将令牌存储到 LocalStorage 或作为 Cookie。在您发出下一个请求时,令牌将作为Authorization
标头的值发送。Java Web 服务将拦截请求并检查令牌的存在和有效性。
You can take a look at this demo applicationI made too.
你也可以看看我制作的这个演示应用程序。