Java JWT实际上是如何与SpringMVC一起创建令牌和验证令牌的?
实际上,我希望能够对Java JWT实际上是如何与SpringMVC一起创建令牌和验证令牌的?,java,spring-mvc,spring-security,jwt,Java,Spring Mvc,Spring Security,Jwt,实际上,我希望能够对JWT概念以及它如何与springmvc配合使用有更多更清晰的理解。我找到了林克,他的程序运行得非常好但我想知道它是如何创建令牌的,然后用户是如何登录到应用程序的。我需要一些澄清/回答它是如何工作的?请做必要的事 我的理解是:在我看来,当启动应用程序时,会调用GenericFilterBean实现类,该类将生成JWT令牌并将其发送到本地存储中的UI(虽然不确定),然后该令牌将与请求一起进入标头,然后它将得到验证,并将访问权授予用户 我想放一些代码片段供参考(甚至你们可以从上面
JWT
概念以及它如何与springmvc
配合使用有更多更清晰的理解。我找到了林克,他的程序运行得非常好但我想知道它是如何创建令牌的,然后用户是如何登录到应用程序的。我需要一些澄清/回答它是如何工作的?请做必要的事
我的理解是:在我看来,当启动应用程序时,会调用GenericFilterBean实现类,该类将生成JWT令牌并将其发送到本地存储中的UI(虽然不确定),然后该令牌将与请求一起进入标头,然后它将得到验证,并将访问权授予用户
我想放一些代码片段供参考(甚至你们可以从上面提到的链接中查找代码)
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));
}
}
当您执行登录时,通过HTTP post请求发送用户名和密码。如果凭据正确,JavaWeb服务将使用令牌进行响应。前端AngularJS应用程序将令牌存储到本地存储或作为Cookie。在您发出下一个请求时,令牌将作为
Authorization
头的值发送。JavaWeb服务将拦截请求并检查令牌的存在和有效性
你也可以看看我做的这个。我是你给出的链接()的作者 示例应用程序使用Jjwt库创建和解密JSON web令牌。在我的示例应用程序中,令牌是在用户成功登录时创建的。这发生在
UserController.java
中的login()
方法中。成功登录(为了简单起见,示例应用程序不处理密码等愚蠢的东西)会返回带有此令牌的LoginResponse
。angular应用程序会将其设置为随每个请求一起发送的默认标头。如果您不想让他们在点击F5后再次登录,您可以将其存储在本地存储或cookie中。再一次;这是一个让事情尽可能简单的例子;我故意把这类事情漏掉了
头由JwtFilter
类读取并存储在请求上下文中。这样,任何路径都可以访问此信息,而不必解密报头本身
尽管所有的事情都在相应的章节中进行了相当深入的解释:如果有什么你不明白的,请告诉我 它是被创造出来的,还是曾经存在过?请尝试将其从本地存储或cookie中删除。谢谢。在登录之前,我清除了缓存,没有看到任何令牌。我也将删除EDIT-1。不客气。:)如果它对你有帮助的话,考虑接受答案。是的,会的,但我还有最后一个问题要问。似乎GenericFilterBean的实现类被多次调用,并且loadByUsername也被调用。你知道为什么吗?(请也更新你的答案)我还创建了EDIT-1来显示一些日志作为证据。不确定是否诚实。似乎一切都很好,从您发布的链接应用程序。请随意使用我的演示应用程序中的代码。谢谢。很高兴能与大家分享关于“Jot”的知识。我喜欢这种方法,我也在我的应用程序中实现了它。快乐学习!!
<?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>
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 ----