Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/spring-mvc/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java JWT实际上是如何与SpringMVC一起创建令牌和验证令牌的?_Java_Spring Mvc_Spring Security_Jwt - Fatal编程技术网

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 ----