• SpringSecurity之记住我功能的实现


    Spring security记住我基本原理:

    登录的时候,请求发送给过滤器UsernamePasswordAuthenticationFilter,当该过滤器认证成功后,会调用RememberMeService,会生成一个token,将token写入到浏览器cookie,同时RememberMeService里边还有个TokenRepository,将token和用户信息写入到数据库中。这样当用户再次访问系统,访问某一个接口时,会经过一个RememberMeAuthenticationFilter的过滤器,他会读取cookie中的token,交给RememberService,RememberService会用TokenRepository根据token从数据库中查是否有记录,如果有记录会把用户名取出来,再调用UserDetailService根据用户名获取用户信息,然后放在SecurityContext里。

    image.png

    RememberMeAuthenticationFilter在Spring Security中认证过滤器链的倒数第二个过滤器位置,当其他认证过滤器都没法认证成功的时候,就会调用RememberMeAuthenticationFilter尝试认证。

    image.png

    实现:

    1,登录表单加上,SpringSecurity在SpringSessionRememberMeServices类里定义了一个常量,默认值就是remember-me

    2,根据上边的原理图可知,要配置TokenRepository,把生成的token存进数据库,这是一个配置bean的配置,放在了BrowserSecurityConfig里

    3,在configure里配置

    4,在BrowserProperties里加上自动登录时间,把记住我时间做成可配置的

    //记住我秒数配置
    private int rememberMeSeconds = 10;

    以下是相关的配置

    pom.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>urity</groupId>
        <artifactId>demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
        <name>demo</name>
        <description>Demo project for Spring Boot</description>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.3.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
            <!--配置支持jsp-->
            <dependency>
                <groupId>org.apache.tomcat.embed</groupId>
                <artifactId>tomcat-embed-jasper</artifactId>
                <version>8.5.12</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>jstl</artifactId>
                <version>1.2</version>
            </dependency>
    
            <!--添加static和templates的依赖-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
    
            <dependency>
                <!-- 由于我使用的spring boot所以我是引入spring-boot-starter-security而且我使用了spring io所以不需要填写依赖的版本号 -->
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>1.7.25</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.22</version>
            </dependency>
    
            <!--mybatis与mysql-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.2.0</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
            <!--druid依赖-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.25</version>
            </dependency>
    
            <!-- spring social相关 -->
            <dependency>
                <groupId>org.springframework.social</groupId>
                <artifactId>spring-social-config</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.social</groupId>
                <artifactId>spring-social-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.social</groupId>
                <artifactId>spring-social-web</artifactId>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>5.0.6.RELEASE</version>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    

    SecurityConfiguration:

    package urity.demo.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.builders.WebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
    import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
    
    import javax.annotation.Resource;
    import javax.sql.DataSource;
    
    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    
        @Resource
        private DataSource dataSource;
    
        @Resource
        private UserDetailsService myUserDetailsService;
    
        /**
         * 配置TokenRepository
         *
         * @return
         */
        @Bean
        public PersistentTokenRepository persistentTokenRepository() {
            JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
            // 配置数据源
            jdbcTokenRepository.setDataSource(dataSource);
            // 第一次启动的时候自动建表(可以不用这句话,自己手动建表,源码中有语句的)
    //         jdbcTokenRepository.setCreateTableOnStartup(true);
            return jdbcTokenRepository;
        }
    
    
        // 处理密码加密解密逻辑
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
    
        //验证相关
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            super.configure(auth);
        }
    
        //浏览器相关
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                    .antMatchers("/hello", "/login.html").permitAll()
                    .anyRequest().authenticated()
                    .and()
                    .formLogin()
                    //指定登录页的路径
                    .loginPage("/hello")
                    //指定自定义form表单请求的路径
                    .loginProcessingUrl("/authentication/form")
                    .failureUrl("/login?error")
                    .defaultSuccessUrl("/success")
                    //必须允许所有用户访问我们的登录页(例如未验证的用户,否则验证流程就会进入死循环)
                    //这个formLogin().permitAll()方法允许所有用户基于表单登录访问/login这个page。
                    .permitAll()
                    .and()
                    .rememberMe()                                   // 记住我相关配置
                    .tokenRepository(persistentTokenRepository())
                    .tokenValiditySeconds(1209600)
            ;
    
    
            //默认都会产生一个hiden标签 里面有安全相关的验证 这边我们不需要 可禁用掉
            http.csrf().disable();
    
        }
        //web安全相关
        @Override
        public void configure(WebSecurity web) throws Exception {
            super.configure(web);
        }
    }
    

    MyUserDetailService:

    package urity.demo.support;
    
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Component;
    import urity.demo.entity.User;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.logging.Logger;
    
    //自定义用户处理的逻辑
    //用户的信息的service
    @Component
    public class MyUserDetailService implements UserDetailsService {
        /**
         * 日志处理类
         */
        private org.slf4j.Logger logger =  LoggerFactory.getLogger(this.getClass());
    
        @Autowired
        private PasswordEncoder passwordEncoder;
    
    
        /**
         * 根据用户名加载用户信息
         *
         * @param username 用户名
         * @return UserDetails
         * @throws UsernameNotFoundException
         */
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            logger.info("表单登录用户名:" + username);
            System.out.println("表单登录用户名:" + username);
            List<GrantedAuthority> grantedAuthorityList = new ArrayList<>();
            grantedAuthorityList.add(new GrantedAuthority() {
                @Override
                public String getAuthority() {
                    return "admin";
                }
            });
    
           User user = new User();
           user.setUsername("test");
           user.setPassword("123");
            String pWord =passwordEncoder.encode(user.getPassword());
            System.out.println("表单登录加密后密码:" + pWord);
            System.out.println("库中的username:"+user.getUsername());
            if(username.equals(user.getUsername())) {
                MyUser myUser = new MyUser(user.getUsername(), pWord, true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_USER"));
                return myUser;
            }else {
                throw  new UsernameNotFoundException("用户["+username+"]不存在");
            }
    
        }
    }
    
    

    MyUser:

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.springframework.security.core.CredentialsContainer;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.SpringSecurityCoreVersion;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.crypto.factory.PasswordEncoderFactories;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.util.Assert;
    
    import java.io.Serializable;
    import java.util.*;
    import java.util.function.Function;
    
    public class MyUser implements UserDetails, CredentialsContainer {
    
        private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
    
        // ~ Instance fields
        // ================================================================================================
        private String password;
        private final String username;
        private final Set<GrantedAuthority> authorities;
        private final boolean accountNonExpired;
        private final boolean accountNonLocked;
        private final boolean credentialsNonExpired;
        private final boolean enabled;
    
        // ~ Constructors
        // ===================================================================================================
    
        /**
         * Calls the more complex constructor with all boolean arguments set to {@code true}.
         */
        public MyUser(String username, String password,
                                 Collection<? extends GrantedAuthority> authorities) {
            this(username, password, true, true, true, true, authorities);
        }
    
        /**
         * Construct the <code>User</code> with the details required by
         * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider}.
         *
         * @param username the username presented to the
         * <code>DaoAuthenticationProvider</code>
         * @param password the password that should be presented to the
         * <code>DaoAuthenticationProvider</code>
         * @param enabled set to <code>true</code> if the user is enabled
         * @param accountNonExpired set to <code>true</code> if the account has not expired
         * @param credentialsNonExpired set to <code>true</code> if the credentials have not
         * expired
         * @param accountNonLocked set to <code>true</code> if the account is not locked
         * @param authorities the authorities that should be granted to the caller if they
         * presented the correct username and password and the user is enabled. Not null.
         *
         * @throws IllegalArgumentException if a <code>null</code> value was passed either as
         * a parameter or as an element in the <code>GrantedAuthority</code> collection
         */
        public MyUser(String username, String password, boolean enabled,
                                 boolean accountNonExpired, boolean credentialsNonExpired,
                                 boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
    
            if (((username == null) || "".equals(username)) || (password == null)) {
                throw new IllegalArgumentException(
                        "Cannot pass null or empty values to constructor");
            }
    
            this.username = username;
            this.password = password;
            this.enabled = enabled;
            this.accountNonExpired = accountNonExpired;
            this.credentialsNonExpired = credentialsNonExpired;
            this.accountNonLocked = accountNonLocked;
            this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
        }
    
        // ~ Methods
        // ========================================================================================================
    
        public Collection<GrantedAuthority> getAuthorities() {
            return authorities;
        }
    
        public String getPassword() {
            return password;
        }
    
        public String getUsername() {
            return username;
        }
    
        public boolean isEnabled() {
            return enabled;
        }
    
        public boolean isAccountNonExpired() {
            return accountNonExpired;
        }
    
        public boolean isAccountNonLocked() {
            return accountNonLocked;
        }
    
        public boolean isCredentialsNonExpired() {
            return credentialsNonExpired;
        }
    
        public void eraseCredentials() {
            password = null;
        }
    
        private static SortedSet<GrantedAuthority> sortAuthorities(
                Collection<? extends GrantedAuthority> authorities) {
            Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
            // Ensure array iteration order is predictable (as per
            // UserDetails.getAuthorities() contract and SEC-717)
            SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<GrantedAuthority>(
                    new MyUser.AuthorityComparator());
    
            for (GrantedAuthority grantedAuthority : authorities) {
                Assert.notNull(grantedAuthority,
                        "GrantedAuthority list cannot contain any null elements");
                sortedAuthorities.add(grantedAuthority);
            }
    
            return sortedAuthorities;
        }
    
        private static class AuthorityComparator implements Comparator<GrantedAuthority>,
                Serializable {
            private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;
    
            public int compare(GrantedAuthority g1, GrantedAuthority g2) {
                // Neither should ever be null as each entry is checked before adding it to
                // the set.
                // If the authority is null, it is a custom authority and should precede
                // others.
                if (g2.getAuthority() == null) {
                    return -1;
                }
    
                if (g1.getAuthority() == null) {
                    return 1;
                }
    
                return g1.getAuthority().compareTo(g2.getAuthority());
            }
        }
    
        /**
         * Returns {@code true} if the supplied object is a {@code User} instance with the
         * same {@code username} value.
         * <p>
         * In other words, the objects are equal if they have the same username, representing
         * the same principal.
         */
        @Override
        public boolean equals(Object rhs) {
            if (rhs instanceof MyUser) {
                return username.equals(((MyUser) rhs).username);
            }
            return false;
        }
    
        /**
         * Returns the hashcode of the {@code username}.
         */
        @Override
        public int hashCode() {
            return username.hashCode();
        }
    
        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(super.toString()).append(": ");
            sb.append("Username: ").append(this.username).append("; ");
            sb.append("Password: [PROTECTED]; ");
            sb.append("Enabled: ").append(this.enabled).append("; ");
            sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; ");
            sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired)
                    .append("; ");
            sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; ");
    
            if (!authorities.isEmpty()) {
                sb.append("Granted Authorities: ");
    
                boolean first = true;
                for (GrantedAuthority auth : authorities) {
                    if (!first) {
                        sb.append(",");
                    }
                    first = false;
    
                    sb.append(auth);
                }
            }
            else {
                sb.append("Not granted any authorities");
            }
    
            return sb.toString();
        }
    
        public static MyUser.UserBuilder withUsername(String username) {
            return new MyUser.UserBuilder().username(username);
        }
    
        /**
         * Builds the user to be added. At minimum the username, password, and authorities
         * should provided. The remaining attributes have reasonable defaults.
         */
        public static class UserBuilder {
            private String username;
            private String password;
            private List<GrantedAuthority> authorities;
            private boolean accountExpired;
            private boolean accountLocked;
            private boolean credentialsExpired;
            private boolean disabled;
    
            /**
             * Creates a new instance
             */
            private UserBuilder() {
            }
    
            /**
             * Populates the username. This attribute is required.
             *
             * @param username the username. Cannot be null.
             * @return the {@link User.UserBuilder} for method chaining (i.e. to populate
             * additional attributes for this user)
             */
            private MyUser.UserBuilder username(String username) {
                Assert.notNull(username, "username cannot be null");
                this.username = username;
                return this;
            }
    
            /**
             * Populates the password. This attribute is required.
             *
             * @param password the password. Cannot be null.
             * @return the {@link User.UserBuilder} for method chaining (i.e. to populate
             * additional attributes for this user)
             */
            public MyUser.UserBuilder password(String password) {
                Assert.notNull(password, "password cannot be null");
                this.password = password;
                return this;
            }
    
            /**
             * Populates the roles. This method is a shortcut for calling
             * {@link #authorities(String...)}, but automatically prefixes each entry with
             * "ROLE_". This means the following:
             *
             * <code>
             *     builder.roles("USER","ADMIN");
             * </code>
             *
             * is equivalent to
             *
             * <code>
             *     builder.authorities("ROLE_USER","ROLE_ADMIN");
             * </code>
             *
             * <p>
             * This attribute is required, but can also be populated with
             * {@link #authorities(String...)}.
             * </p>
             *
             * @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null,
             * contain null values or start with "ROLE_"
             * @return the {@link User.UserBuilder} for method chaining (i.e. to populate
             * additional attributes for this user)
             */
            public MyUser.UserBuilder roles(String... roles) {
                List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(
                        roles.length);
                for (String role : roles) {
                    Assert.isTrue(!role.startsWith("ROLE_"), role
                            + " cannot start with ROLE_ (it is automatically added)");
                    authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
                }
                return authorities(authorities);
            }
    
            /**
             * Populates the authorities. This attribute is required.
             *
             * @param authorities the authorities for this user. Cannot be null, or contain
             * null values
             * @return the {@link User.UserBuilder} for method chaining (i.e. to populate
             * additional attributes for this user)
             * @see #roles(String...)
             */
            public MyUser.UserBuilder authorities(GrantedAuthority... authorities) {
                return authorities(Arrays.asList(authorities));
            }
    
            /**
             * Populates the authorities. This attribute is required.
             *
             * @param authorities the authorities for this user. Cannot be null, or contain
             * null values
             * @return the {@link User.UserBuilder} for method chaining (i.e. to populate
             * additional attributes for this user)
             * @see #roles(String...)
             */
            public MyUser.UserBuilder authorities(List<? extends GrantedAuthority> authorities) {
                this.authorities = new ArrayList<GrantedAuthority>(authorities);
                return this;
            }
    
            /**
             * Populates the authorities. This attribute is required.
             *
             * @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN,
             * etc). Cannot be null, or contain null values
             * @return the {@link User.UserBuilder} for method chaining (i.e. to populate
             * additional attributes for this user)
             * @see #roles(String...)
             */
            public MyUser.UserBuilder authorities(String... authorities) {
                return authorities(AuthorityUtils.createAuthorityList(authorities));
            }
    
            /**
             * Defines if the account is expired or not. Default is false.
             *
             * @param accountExpired true if the account is expired, false otherwise
             * @return the {@link User.UserBuilder} for method chaining (i.e. to populate
             * additional attributes for this user)
             */
            public MyUser.UserBuilder accountExpired(boolean accountExpired) {
                this.accountExpired = accountExpired;
                return this;
            }
    
            /**
             * Defines if the account is locked or not. Default is false.
             *
             * @param accountLocked true if the account is locked, false otherwise
             * @return the {@link User.UserBuilder} for method chaining (i.e. to populate
             * additional attributes for this user)
             */
            public MyUser.UserBuilder accountLocked(boolean accountLocked) {
                this.accountLocked = accountLocked;
                return this;
            }
    
            /**
             * Defines if the credentials are expired or not. Default is false.
             *
             * @param credentialsExpired true if the credentials are expired, false otherwise
             * @return the {@link User.UserBuilder} for method chaining (i.e. to populate
             * additional attributes for this user)
             */
            public MyUser.UserBuilder credentialsExpired(boolean credentialsExpired) {
                this.credentialsExpired = credentialsExpired;
                return this;
            }
    
            /**
             * Defines if the account is disabled or not. Default is false.
             *
             * @param disabled true if the account is disabled, false otherwise
             * @return the {@link User.UserBuilder} for method chaining (i.e. to populate
             * additional attributes for this user)
             */
            public MyUser.UserBuilder disabled(boolean disabled) {
                this.disabled = disabled;
                return this;
            }
    
            public UserDetails build() {
                return new User(username, password, !disabled, !accountExpired,
                        !credentialsExpired, !accountLocked, authorities);
            }
        }
        }
    

    login.html:

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>第一个HTML页面</title>
    </head>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    自定义表单验证:
    <!--<form name="f" action="/login" method="post">-->
    <!-- <form name="f" action="/authentication/form" method="post">-->
    <form name="f" action="/authentication/form" method="post">
        <br/>
        用户名:
        <input type="text" name="username" placeholder="name"><br/>
        密码:
        <input type="password" name="password" placeholder="password"><br/>
        <input type="checkbox" name="remember-me" value="true">记住我<br/>
        <input name="submit" type="submit" value="提交">
    </form>
    </body>
    </html>
    

    LoginController:

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import urity.demo.entity.User;
    @Controller
    public class LoginController {
    
        @RequestMapping("/hello")
        public String hello() {
            System.out.println("kkkk==");
            return "login";
        }
    
        @RequestMapping("/success")
        public String success(){
    
            return "success";
        }
    
        @RequestMapping("/forkl")
        public String check(User user){
            System.out.println(user);
            return "success";
        }
    
        @RequestMapping("/user")
        public String fuinduser(){
           return "user";
        }
    }
    
    

    user.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    由于用了记住我 所以现在可以直接访问了哦!
    </body>
    </html>
    

    到此我们来启动项目,首次访问http://localhost:8787/user会需要我们登录,这里我们进行登录先不勾选记住我:

    image.png

    登录成功后可以正常访问user,然后我们关闭浏览器重新打开 访问http://localhost:8787/user会被返回到登录的页面,这个就是没有任何效果的演示.

    然后我们再次登录,并勾选记住我:

    image.png

    这里我们登录成功后关闭浏览器再打开 仍然可以访问http://localhost:8787/user,而且不需要登录:

    image.png

    这里浏览器做了如下的事情:

    • 在我们数据库建立表并插入数据

    image.png

    • 然后我们关闭浏览器在访问,它会去库里面查找响应的token,如果有就不用登录直接访问:

    image.png

    到此,rememberme的功能就完成了

  • 相关阅读:
    OAF_文件系列8_实现OAF处理Excel的JXL包详解
    OAF_文件系列7_实现OAF处理Excel的JXL包介绍(概念)
    OAF_文件系列6_实现OAF导出XML文件javax.xml.parsers/transformer(案例)
    Kafka SSL安装与配置
    如何构建推荐系统
    Flink消费Kafka到HDFS实现及详解
    Kafka网络模型和通信流程剖析
    Kafka日志压缩剖析
    Kafka Eagle安装详情及问题解答
    Kafka幂等性原理及实现剖析
  • 原文地址:https://www.cnblogs.com/charlypage/p/9332984.html
Copyright © 2020-2023  润新知