精华内容
下载资源
问答
  • web保持登录状态
    千次阅读
    2018-08-13 15:25:01

    例子有cookie和session的demo,
    1、Cookie
    2、Session
    3、Token (1)、token也一样其实就是一个头字段(或者放在参数上),客户端每次请求时候带上这个头字段,后台进行校验通过则放行,头字段可以放在redis里面实现比较方便,后台也可以实现头字段的Value和用户信息之间的一 一对应关系,自己维护上面的关系
    (2)、现成jar的例子
    资源都是5分 ,多了私聊我 修改

    更多相关内容
  • 先说下背景,项目包含一个管理系统(web)和门户网站(web),还有一个手机APP(包括Android和IOS),三个系统共用一个后端,在后端使用shiro进行登录认证和权限控制。好的,那么问题来了:先说web端1.因为一般网页...

    先说下背景,项目包含一个管理系统(web)和门户网站(web),还有一个手机APP(包括Android和IOS),三个系统共用一个后端,在后端使用shiro进行登录认证和权限控制。好的,那么问题来了

    先说web端

    1.因为一般网页主需要记住7天密码(或者稍微更长)的功能就可以了,可以使用cookie实现,而且shiro也提供了记住密码的功能,在服务器端session不需要保存过长时间。

    再说app端

    2.因为APP免密码登录时间需要较长(在用户不主动退出的时候,应该一直保持登录状态),这样子在服务器端就得把session保存很长时间,给服务器内存和性能上造成较大的挑战,存在的矛盾是:APP需要较长时间的免密码登录,而服务器不能保存过长时间的session。

     解决办法:

    • APP第一次登录,使用用户名和密码,如果登录成功,将cookie保存在APP本地(比如sharepreference),后台将cookie值保存到user表里面
    • APP访问服务器,APP将cookie添加在heade里面,服务器session依然存在,可以正常访问
    • APP访问服务器,APP将cookie添加在heade里面,服务器session过期,访问失败,由APP自动带着保存在本地的cookie去服务器登录,服务器可以根据cookie和用户名进行登录,这样服务器又有session,会生成新的cookie返回给APP,APP更新本地cookie,又可以正常访问
    • 用户手动退出APP,删除APP本次存储的cookie,下次登录使用用户名和密码登录

    这种方法存在的问题:

    1. cookie保存在APP本地,安全性较低,可以通过加密cookie增加安全性
    2. 每次服务器session失效之后,得由APP再次发起登录请求(虽然用户是不知道的),但是这样本身就会增加访问次数,好在请求数量并不是很大,不过这种方式会使cookie经常更新,反而增加了安全性。

    这里给出另外一种实现方式:
    实现自己的SessionDao,将session保存在数据库,这样子的好处是,session不会大量堆积在内存中,就不需要考虑session的过期时间了,对于APP这种需要长期保存session的情况来说,就可以无限期的保存session了,也就不用APP在每次session过期之后重新发送登录请求了。实现方式如下:

    1. 数据库设计 (需要的话自己扩展)

                           

    2. 实体类
    import java.io.Serializable;
    import java.util.Date;
    
    
    
    /**
     * 用户session
     * 
     * @author flyingTiger
     * @email gaofeihu95@163.com
     * @date 2018-03-05 15:44:08
     */
    public class SysUserSessionEntity implements Serializable {
    	private static final long serialVersionUID = 1L;
    	
    	//id
    	private String id;
    	//session
    	private String session;
    	//cookie
    	private String cookie;
    	//user_id
    	private Long userId;
    	//创建时间
    	private Date createTime;
    	//最后更新时间
    	private Date lastUpTime;
    	//状态
    	private String status;
    
    	/**
    	 * 设置:id
    	 */
    	public void setId(String id) {
    		this.id = id;
    	}
    	/**
    	 * 获取:id
    	 */
    	public String getId() {
    		return id;
    	}
    	/**
    	 * 设置:session
    	 */
    	public void setSession(String session) {
    		this.session = session;
    	}
    	/**
    	 * 获取:session
    	 */
    	public String getSession() {
    		return session;
    	}
    	/**
    	 * 设置:cookie
    	 */
    	public void setCookie(String cookie) {
    		this.cookie = cookie;
    	}
    	/**
    	 * 获取:cookie
    	 */
    	public String getCookie() {
    		return cookie;
    	}
    	/**
    	 * 设置:user_id
    	 */
    	public void setUserId(Long userId) {
    		this.userId = userId;
    	}
    	/**
    	 * 获取:user_id
    	 */
    	public Long getUserId() {
    		return userId;
    	}
    	/**
    	 * 设置:创建时间
    	 */
    	public void setCreateTime(Date createTime) {
    		this.createTime = createTime;
    	}
    	/**
    	 * 获取:创建时间
    	 */
    	public Date getCreateTime() {
    		return createTime;
    	}
    	/**
    	 * 设置:最后更新时间
    	 */
    	public void setLastUpTime(Date lastUpTime) {
    		this.lastUpTime = lastUpTime;
    	}
    	/**
    	 * 获取:最后更新时间
    	 */
    	public Date getLastUpTime() {
    		return lastUpTime;
    	}
    	/**
    	 * 设置:状态
    	 */
    	public void setStatus(String status) {
    		this.status = status;
    	}
    	/**
    	 * 获取:状态
    	 */
    	public String getStatus() {
    		return status;
    	}
    }
    

    3. 在此之前我已经实现过sessionDao ,是将session放到redis里面

    import org.apache.shiro.session.Session;
    import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Component;
    
    import java.io.Serializable;
    import java.util.concurrent.TimeUnit;
    
    /**
     * shiro session dao
     *
     * @author flyingTiger
     * @email gaofeihu95@163.com
     * @date  2017/9/27 21:35
     */
    @Component
    public class RedisShiroSessionDAO extends EnterpriseCacheSessionDAO {
        @Autowired
        private RedisTemplate redisTemplate;
    
        //创建session
        @Override
        protected Serializable doCreate(Session session) {
            Serializable sessionId = super.doCreate(session);
            final String key = RedisKeys.getShiroSessionKey(sessionId.toString());
            setShiroSession(key, session);
            return sessionId;
        }
    
        //获取session
        @Override
        protected Session doReadSession(Serializable sessionId) {
            Session session = super.doReadSession(sessionId);
            if(session == null){
                final String key = RedisKeys.getShiroSessionKey(sessionId.toString());
                session = getShiroSession(key);
            }
            return session;
        }
    
        //更新session
        @Override
        protected void doUpdate(Session session) {
            super.doUpdate(session);
            final String key = RedisKeys.getShiroSessionKey(session.getId().toString());
            setShiroSession(key, session);
        }
    
        //删除session
        @Override
        protected void doDelete(Session session) {
            super.doDelete(session);
            final String key = RedisKeys.getShiroSessionKey(session.getId().toString());
            redisTemplate.delete(key);
        }
    
        private Session getShiroSession(String key) {
            return (Session)redisTemplate.opsForValue().get(key);
        }
    
        private void setShiroSession(String key, Session session){
            redisTemplate.opsForValue().set(key, session);
            //60分钟过期
            redisTemplate.expire(key, 60, TimeUnit.MINUTES);
        }
    
    }
    
    4. 那么现在我即要实现将session写入到数据库中,又要将sesion存储到redis里面。我还不想大量修改redisDao的代码,我该怎么做呢?
    答案就是,
        继承,
          1. 首先自己重新定义一个SessionDao比如叫做DataBaseSessionDao.

          2. 然后让redisDao继承DataBaseDao

    DataBaseSessionDao的代码如下

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.session.mgt.ValidatingSession;
    import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.io.Serializable;
    import java.util.HashMap;
    
    /**
     * 存放session 到数据库
     *
     * @author FlyingTiger
     * @version 1.0
     * @since 2018/03/05 14:52
     */
    @Component
    public class DataBaseSessionDao extends EnterpriseCacheSessionDAO {
        public static final String COOKIE = "cookie";
        // 此处虽然不符合三层设计,但有效避免sql注入和减少了更新的维护管理,是合理的。 at FlyingTiger by 2018年3月5日
        @Autowired
        private SysUserSessionService sysUserSessionService;
    
    
        //创建session
        @Override
        protected Serializable doCreate(Session session) {
            Serializable cookie = super.doCreate(session);
            // 保存session到数据库
            SysUserSessionEntity sysUserSession = new SysUserSessionEntity();
            sysUserSession.setCookie(cookie.toString());
            sysUserSession.setSession(SerializableUtils.serializ(session));
            sysUserSessionService.save(sysUserSession);
            return cookie;
        }
    
        //获取session
        @Override
        protected Session doReadSession(Serializable sessionId) {
            Session session = super.doReadSession(sessionId);
            if (session == null) {
                //final String key = RedisKeys.getShiroSessionKey(sessionId.toString());
                HashMap<String, Object> param = new HashMap<>();
                param.put(COOKIE, sessionId);
                SysUserSessionEntity sysUserSessionEntity = sysUserSessionService.queryObjectByMap(param);
                // 如果不为空
                if (sysUserSessionEntity != null) {
                    String sessionStr64 = sysUserSessionEntity.getSession();
                    session = SerializableUtils.deserializ(sessionStr64);
                }
    
            }
            return session;
        }
    
        //更新session
        @Override
        protected void doUpdate(Session session) {
            super.doUpdate(session);
            //当是ValidatingSession 无效的情况下,直接退出
            if (session instanceof ValidatingSession &&
                    !((ValidatingSession) session).isValid()) {
                return;
            }
            //检索到用户名
            // String username = String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY));
            HashMap<String, Object> param = new HashMap<>();
            param.put(COOKIE, session.getId());
            SysUserSessionEntity sysUserSessionEntity = sysUserSessionService.queryObjectByMap(param);
            sysUserSessionEntity.setSession(SerializableUtils.serializ(session));
            // 如果登录成功,更新用户id
            if (ShiroUtils.getSubject().isAuthenticated()){
                SysUserEntity sysUser = (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
                sysUserSessionEntity.setUserId(sysUser.getUserId());
            }
    
            sysUserSessionService.update(sysUserSessionEntity);
        }
    
        //删除session
        @Override
        protected void doDelete(Session session) {
            super.doDelete(session);
            HashMap<String, Object> param = new HashMap<>();
            param.put(COOKIE, session.getId());
            SysUserSessionEntity sysUserSessionEntity = sysUserSessionService.queryObjectByMap(param);
            if (sysUserSessionEntity != null) {
                sysUserSessionService.delete(sysUserSessionEntity.getId());
            }
        }
    然后让redisSessionDao 继承 DataBaseSessionDao (除了继承关系,其他都不变 序列化类参考标题7):
    import org.apache.shiro.session.Session;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Component;
    
    import java.io.Serializable;
    import java.util.concurrent.TimeUnit;
    
    /**
     * shiro session dao
     *
     * @author flyingTiger
     * @email gaofeihu95@163.com
     * @date  2017/9/27 21:35
     */
    @Component
    public class RedisShiroSessionDAO extends DataBaseSessionDao {
        @Autowired
        private RedisTemplate redisTemplate;
    
        //创建session
        @Override
        protected Serializable doCreate(Session session) {
            Serializable sessionId = super.doCreate(session);
            final String key = RedisKeys.getShiroSessionKey(sessionId.toString());
            setShiroSession(key, session);
            return sessionId;
        }
    
        //获取session
        @Override
        protected Session doReadSession(Serializable sessionId) {
            Session session = super.doReadSession(sessionId);
            if(session == null){
                final String key = RedisKeys.getShiroSessionKey(sessionId.toString());
                session = getShiroSession(key);
            }
            return session;
        }
    
        //更新session
        @Override
        protected void doUpdate(Session session) {
            super.doUpdate(session);
            final String key = RedisKeys.getShiroSessionKey(session.getId().toString());
            setShiroSession(key, session);
        }
    
        //删除session
        @Override
        protected void doDelete(Session session) {
            super.doDelete(session);
            final String key = RedisKeys.getShiroSessionKey(session.getId().toString());
            redisTemplate.delete(key);
        }
    
        private Session getShiroSession(String key) {
            return (Session)redisTemplate.opsForValue().get(key);
        }
    
        private void setShiroSession(String key, Session session){
            redisTemplate.opsForValue().set(key, session);
            //60分钟过期
            redisTemplate.expire(key, 60, TimeUnit.MINUTES);
        }
    
    }
    
    5.  这里用到的是shiro bean  配置 (配置文件请参考标题6)

    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.mgt.SessionManager;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * Shiro的配置文件
     *
     * @author flyingTiger
     * @email gaofeihu95@163.com
     * @date 2017/9/27 22:02
     */
    @Configuration
    public class ShiroConfig {
    
        @Bean("sessionManager")
        public SessionManager sessionManager(RedisShiroSessionDAO redisShiroSessionDAO, DataBaseSessionDao dataBaseSessionDao, @Value("${renren.redis.open}") boolean redisOpen,
                                             @Value("${renren.shiro.redis}") boolean shiroRedis) {
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            //设置session过期时间为1小时(单位:毫秒),默认为30分钟
            sessionManager.setGlobalSessionTimeout(60 * 60 * 1000);
            sessionManager.setSessionValidationSchedulerEnabled(true);
            sessionManager.setSessionIdUrlRewritingEnabled(false);
    
            //如果开启redis缓存且shiro.redis=true,则shiro session存到redis里
            if (redisOpen && shiroRedis) {
                sessionManager.setSessionDAO(redisShiroSessionDAO);
            } else {
                // 将session保存到数据库
                sessionManager.setSessionDAO(dataBaseSessionDao);
            }
            return sessionManager;
        }
    
    }

    6. 配置spring-shiro.xml配置文件(如果不是用bean配置)

    注意这个sessionDao 里面配置了activeSessionsCacheName 这个属性,这个在ecache.xml里面必须也配置一个shiro-activeSessionCache节点,用于存激活的session,简单来讲,就是登录的用户。

    <!-- 给予shior的内存缓存系统 ,这个是shiro的缓存服务-->
        <!-- <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />  
         --> 
    
        <!--  
             配置 CacheManager. 
        -->     
        <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
            <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> 
        </bean>
    
    
        <!-- 会话Session ID生成器 -->
        <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
    
        <!-- 创建SessionDao  这个SessionDao继承了 EnterpriseCacheSessionDAO-->
        <bean id="sessionDao" class="com.yellowcong.shiro.dao.SessionDao">
            <!-- 配置SessionDao里面的 id生成器 -->
            <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
            <!-- 设置Session缓存到Eacehc的名字,默认就是shiro-activeSessionCache -->
            <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
        </bean>
    
        <!-- 配置Session管理 -->           
        <!-- org.apache.shiro.session.mgt.DefaultSessionManager
        DefaultWebSessionManager 继承了 DefaultSessionManager 这个类
         -->
        <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
            <!-- session超时 30 分钟 -->
            <property name="globalSessionTimeout" value="1800000"/>
    
            <property name="deleteInvalidSessions" value="true"/>
    
            <!-- Session调度器,用来检查Session是否还存在的问题 -->
            <property name="sessionValidationSchedulerEnabled" value="true"/>
            <!--  设定session的调度管理器-->
            <property name="sessionValidationScheduler" ref="sessionValidationScheduler" />
            <!-- 配置管理session的 dao -->
            <property name="sessionDAO" ref="sessionDao"/>
    
            <!-- 开启cookie 不然一直登录不上 -->
            <property name="sessionIdCookieEnabled" value="true"/>
            <property name="sessionIdCookie" ref="sessionIdCookie"/>
        </bean>
    
        <!-- 全局的会话信息检测扫描信息间隔30分钟 
             设定检查sesion过期事件,
        -->
        <bean id="sessionValidationScheduler"
            class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
            <property name="sessionValidationInterval" value="1800000" />
            <property name="sessionManager" ref="sessionManager" />
        </bean>
    
        <!-- 安全管理器 -->    
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
            <!-- 缓存管理器 -->
            <property name="cacheManager" ref="cacheManager" />  
    
            <!-- 验证 -->        
            <property name="authenticator" ref="authenticator"/>
    
            <!-- 多个验证策略 realmes -->
            <property name="realms">
                <list>
                    <!-- 这个认证,有一个先后的顺序 -->
                    <ref bean="sampleRealm1"/>
                    <ref bean="sampleRealm2"/>
                </list>
            </property>
    
            <!-- 配置SessionManager -->
          <property name="sessionManager" ref="sessionManager"/> 
        </bean> 

    下面是完整配置

    其中还有一部分是关于Shiro生命周期的,存储在了Spring-mvc中,因为生命周期配置在spring-shiro.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:aop="http://www.springframework.org/schema/aop"
        xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        <description>== Shiro Components ==</description>
    
        <!-- 给予shior的内存缓存系统 ,这个是shiro的缓存服务-->
        <!-- <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />  
         --> 
    
        <!--  
             配置 CacheManager. 
        -->     
        <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
            <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/> 
        </bean>
    
    
        <!-- 会话Session ID生成器 -->
        <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
    
        <!-- 创建SessionDao  这个SessionDao继承了 EnterpriseCacheSessionDAO-->
        <bean id="sessionDao" class="com.yellowcong.shiro.dao.SessionDao">
            <!-- 配置SessionDao里面的 id生成器 -->
            <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
            <!-- 设置Session缓存到Eacehc的名字,默认就是shiro-activeSessionCache -->
            <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
        </bean>
    
        <!-- 配置Session管理 -->           
        <!-- org.apache.shiro.session.mgt.DefaultSessionManager
        DefaultWebSessionManager 继承了 DefaultSessionManager 这个类
         -->
        <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
            <!-- session超时 30 分钟 -->
            <property name="globalSessionTimeout" value="1800000"/>
    
            <property name="deleteInvalidSessions" value="true"/>
    
            <!-- Session调度器,用来检查Session是否还存在的问题 -->
            <property name="sessionValidationSchedulerEnabled" value="true"/>
            <!--  设定session的调度管理器-->
            <property name="sessionValidationScheduler" ref="sessionValidationScheduler" />
            <!-- 配置管理session的 dao -->
            <property name="sessionDAO" ref="sessionDao"/>
    
            <!-- 开启cookie 不然一直登录不上 -->
            <property name="sessionIdCookieEnabled" value="true"/>
            <property name="sessionIdCookie" ref="sessionIdCookie"/>
        </bean>
    
        <!-- 全局的会话信息检测扫描信息间隔30分钟 
             设定检查sesion过期事件,
        -->
        <bean id="sessionValidationScheduler"
            class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
            <property name="sessionValidationInterval" value="1800000" />
            <property name="sessionManager" ref="sessionManager" />
        </bean>
    
        <!-- 安全管理器 -->    
        <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">  
            <!-- 缓存管理器 -->
            <property name="cacheManager" ref="cacheManager" />  
    
            <!-- 验证 -->        
            <property name="authenticator" ref="authenticator"/>
    
            <!-- 多个验证策略 realmes -->
            <property name="realms">
                <list>
                    <!-- 这个认证,有一个先后的顺序 -->
                    <ref bean="sampleRealm1"/>
                    <ref bean="sampleRealm2"/>
                </list>
            </property>
    
            <!-- 配置SessionManager -->
          <property name="sessionManager" ref="sessionManager"/> 
        </bean>  
    
    
        <!-- 会话Cookie模板 -->
        <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
            <constructor-arg value="sid"/>
            <property name="httpOnly" value="true"/>
            <property name="maxAge" value="-1"/>
        </bean>
    
        <!-- 授权策略 -->
        <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
            <property name="authenticationStrategy" >
                <!-- 所有Reaml都全部匹配的策略 -->
                <!-- <bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"/> -->
                <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"/>
            </property>
        </bean>
    
        <!-- 授权 认证 ,自己定义的,领域(Realm),shiro需要配置一个领域(Realm),以便我们可以访问用户-->
        <bean id="sampleRealm1" class=" com.yellowcong.shiro.realm.SampleRealm" >
    
            <!-- 如果不加入密码匹配的操作,密码就不会存在 -->
            <!-- 加入了密码匹配器之后,就会默认将前台传递过来的密码自动MD5加密 -->
            <property name="credentialsMatcher">
                <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                    <!-- 加密的方式 -->
                    <constructor-arg index="0" type="java.lang.String" value="MD5" />
                    <!-- 加密的次数,默认是1次 -->
                    <property name="hashIterations" value="1"/>
                </bean>
            </property>
        </bean>
    
    
        <!-- 授权 认证 ,自己定义的,领域(Realm),shiro需要配置一个领域(Realm),以便我们可以访问用户-->
        <bean id="sampleRealm2" class=" com.yellowcong.shiro.realm.SampleRealm2" >
    
            <!-- 如果不加入密码匹配的操作,密码就不会存在 -->
            <!-- 加入了密码匹配器之后,就会默认将前台传递过来的密码自动SHA1加密 -->
            <property name="credentialsMatcher">
                <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                    <!-- 加密的方式 -->
                    <constructor-arg index="0" type="java.lang.String" value="SHA1" />
                    <!-- 加密的次数,默认是1次 -->
                    <property name="hashIterations" value="1"/>
                </bean>
            </property>
        </bean>
    
        <!-- Shior的过滤器配置 -->
        <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
           <property name="securityManager" ref="securityManager" />  
           <!-- 用户登录地址 -->
           <property name="loginUrl" value="/user/login" />  
           <!-- 登录成功 -->
           <property name="successUrl" value="/user/list" />  
           <!-- 未授权的钦奎光 -->
           <property name="unauthorizedUrl" value="/user/error" />  
    
           <!-- 通过工厂模式,获取数据库里面 权限配置-->
           <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
       </bean> 
    
       <!-- 获取Bean里面的Map集合 -->
       <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionsMapBuilder" factory-method="loadFilterChainDefinitions"/>
    
       <!-- 读取初始自定义权限内容-->
       <bean id="filterChainDefinitionsMapBuilder" class="com.yellowcong.shiro.filter.FilterChainDefinitionsMapBuilder" />
    </beans>
    spring-mvc的配置

    <!-- Shiro生命周期处理器-->
       <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
    
        <!-- AOP式方法级权限检查 -->  
        <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"  
            depends-on="lifecycleBeanPostProcessor">  
            <property name="proxyTargetClass" value="true" />  
        </bean>  
    
        <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">  
            <property name="securityManager" ref="securityManager" />  
        </bean>     

    配置ecache (可选session缓存)


    <ehcache>
    
        <!-- Sets the path to the directory where cache .data files are created.
    
             If the path is a Java System Property it is replaced by
             its value in the running VM.
    
             The following properties are translated:
             user.home - User's home directory
             user.dir - User's current working directory
             java.io.tmpdir - Default temp file path -->
        <diskStore path="java.io.tmpdir"/>
    
        <cache name="authorizationCache"
               eternal="false"
               timeToIdleSeconds="3600"
               timeToLiveSeconds="0"
               overflowToDisk="false"
               statistics="true">
        </cache>
    
        <cache name="authenticationCache"
               eternal="false"
               timeToIdleSeconds="3600"
               timeToLiveSeconds="0"
               overflowToDisk="false"
               statistics="true">
        </cache>
    
        <cache name="shiro-activeSessionCache"
               eternal="false"
               timeToIdleSeconds="3600"
               timeToLiveSeconds="0"
               overflowToDisk="false"
               statistics="true">
        </cache>
    
        <!--Default Cache configuration. These will applied to caches programmatically created through
            the CacheManager.
    
            The following attributes are required for defaultCache:
    
            maxInMemory       - Sets the maximum number of objects that will be created in memory
            eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                                is never expired.
            timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                                if the element is not eternal. Idle time is now - last accessed time
            timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                                if the element is not eternal. TTL is now - creation time
            overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                                has reached the maxInMemory limit.
    
            -->
        <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            />
    
        <!--Predefined caches.  Add your cache configuration settings here.
            If you do not have a configuration for your cache a WARNING will be issued when the
            CacheManager starts
    
            The following attributes are required for defaultCache:
    
            name              - Sets the name of the cache. This is used to identify the cache. It must be unique.
            maxInMemory       - Sets the maximum number of objects that will be created in memory
            eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                                is never expired.
            timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                                if the element is not eternal. Idle time is now - last accessed time
            timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                                if the element is not eternal. TTL is now - creation time
            overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                                has reached the maxInMemory limit.
    
            -->
    
        <!-- Sample cache named sampleCache1
            This cache contains a maximum in memory of 10000 elements, and will expire
            an element if it is idle for more than 5 minutes and lives for more than
            10 minutes.
    
            If there are more than 10000 elements it will overflow to the
            disk cache, which in this configuration will go to wherever java.io.tmp is
            defined on your system. On a standard Linux system this will be /tmp"
            -->
      <!--   <cache name="sampleCache1"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="300"
            timeToLiveSeconds="600"
            overflowToDisk="true"
            />
     -->
        <!-- Sample cache named sampleCache2
            This cache contains 1000 elements. Elements will always be held in memory.
            They are not expired. -->
    
        <!-- Place configuration for your caches following -->
    
    </ehcache>

    7.  SerializableUtils

    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.util.Base64;
    
    import org.apache.shiro.session.Session;
    
    /**
     * 序列和反序列Session对象,只有将session对象序列化成字符串,才可以存储到Mysql上,不能直接存
     *
     * @author FlyingTiger
     * @version 1.0
     * @since 2018/03/05 15:59
     */
    public class SerializableUtils {
    
    
        /**
         * 创建日期:2017/12/21<br/>
         * 创建时间:9:25:30<br/>
         * 创建用户:FlyingTiger<br/>
         * 机能概要:将Session序列化成String类型
         *
         * @param session
         * @return
         */
        public static String serializ(Session session) {
            try {
                //ByteArrayOutputStream 用于存储序列化的Session对象
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
    
                //将Object对象输出成byte数据
                ObjectOutputStream out = new ObjectOutputStream(bos);
                out.writeObject(session);
    
                //将字节码,编码成String类型数据
                return Base64.getEncoder().encodeToString(bos.toByteArray());
            } catch (Exception e) {
                throw new RuntimeException("序列化失败");
            }
        }
    
    
        /**
         * 创建日期:2017/12/21<br/>
         * 创建时间:9:26:19<br/>
         * 创建用户:FlyingTiger<br/>
         * 机能概要:将一个Session的字符串序列化成字符串,反序列化
         *
         * @param sessionStr
         * @return
         */
        public static Session deserializ(String sessionStr) {
            try {
                //读取字节码表
                ByteArrayInputStream bis = new ByteArrayInputStream(Base64.getDecoder().decode(sessionStr));
    
                //将字节码反序列化成 对象
                ObjectInputStream in = new ObjectInputStream(bis);
                Session session = (Session) in.readObject();
                return session;
            } catch (Exception e) {
                throw new RuntimeException("反序列化失败");
            }
        }
    }



    8.  用户登录成功后,将JSESSIONID 下发到终端,存储到移动端应用层变量。 每次访问的时候带上此sessionId 即可保持 用户登录。

    PS:

    • 在用户使用用户名和密码登录的时候,对密码进行加密
    • 会话保持如果使用cookie这种技术的话,存在被别人截取cookie之后就可以认证登录了
    • 在本地保存密码肯定是不合适的,如果保存cookie(token)的话,手机被root之后,很容易就可以看得到了,比如Android的就只是一个xml文件,所以cookie保存要加密,加密之后提高了破解门槛,加密就涉及到秘钥的问题了,秘钥如果写在代码里面,java被反编译之后就很容易秘钥找得到了,当然了google早就已经开始支持NDK(即Android原生开发,这个原生是指使用C/C++开发,编译成为so文件,在java中调用),这样又加大了破解难度,使用Hybrid就更不用说了,直接解压安装包就可以看到了。
    • cookie如果保存在本地,更新的时机(频率)是什么,这样就算是cookie泄露了,也只是在某一段时间内有用(当然了,对于“有心人”来说“这段时间”已经足够做一些事儿了)
    • 可以在session表中添加ip地址来进行再次判断,这样可以解决上述问题。
        参考:

       shiro实现APP、web统一登录认证和权限管理

        Shiro之保存Session到数据库中-yellowcong

    展开全文
  • Springboot-登录功能-拦截器-保持登录状态 在原先的简单方法后,完善了功能 前文登录功能:https://blog.csdn.net/weixin_45910779/article/details/113576610 本人博客园:...

    Springboot-登录功能-拦截器-保持登录状态

    在原先的简单方法后,完善了功能

    前文登录功能:https://blog.csdn.net/weixin_45910779/article/details/113576610   

    本人博客园:https://www.cnblogs.com/djhzzl/p/14135784.html

    在原先代码上添加拦截器和session,保持登录状态

    拦截器实现:

    使用拦截器,保证部分功能需要先登录,才能访问

    新建拦截器

    ·自定义拦截器,需要继承HandlerInterceptorAdapter并重写preHandle方法

    ·同时需要设置不拦截页面静态资源,否则会加载不到图片、css等静态资源

    ·还需要获取用户登录信息,如果获取不到,则跳转到登录界面。

    ·代码如下:

    package com.hut.maoyanmovie.interceptor;
    
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    /**
     * @author HP
     * @data 2020-12-14
     *
     *新建拦截器
     *
     * 自定义拦截器
     */
    public class AuthInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 拦截处理代码
    
            //静态资源不被拦截器拦截
            String uri = request.getRequestURI();
            if (uri.endsWith("js")||uri.endsWith("css")||uri.endsWith("jpg")||uri.endsWith("svg")||uri.endsWith("jpg")||uri.endsWith("png")){
                return true ;
            }
            HttpSession session = request.getSession();
            // 获取用户信息,如果没有用户信息直接返回提示信息
            Object userInfo = session.getAttribute("loginUser");
            if (userInfo == null) {
                request.setAttribute("msg","请先登录!");
                request.getRequestDispatcher("logging").forward(request, response);
                return false;
            } else {
    
            }
    
            return true;
        }
    
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
        }
    }

    注册拦截器

    ·注册自己的拦截器并设置拦截的请求路径

    ·新建配置类继承WebMvcConfigurerAdapter类,重写addInterceptors方法

    ·既然要增加自己的拦截器,那当然要得到springboot加入拦截器的入口,然后把我们自己写的拦截器也注册到springboot中让其起作用

    ·需要加入@Configuration注解,在springboot启动的时候就会该配置类就会被扫描并加载,从而将我们的拦截器注册进去。这时候的拦截器已经可以正常工作了

    package com.hut.maoyanmovie.interceptor;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    /**
     * @author HP
     * @data 2020-12-14
     * 注册拦截器
     * 新建配置类继承WebMvcConfigurerAdapter类,重写addInterceptors方法
     * 既然要增加自己的拦截器,那当然要得到springboot加入拦截器的入口,然后把我们自己写的拦截器也注册到springboot中让其起作用
     * 需要加入@Configuration注解,在springboot启动的时候就会该配置类就会被扫描并加载,从而将我们的拦截器注册进去。这时候的拦截器已经可以正常工作了
     */
    @Configuration
    public class WebAppConfig extends WebMvcConfigurerAdapter {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //注册自己的拦截器并设置拦截的请求路径
            registry.addInterceptor(new AuthInterceptor())
                    .addPathPatterns("/interorders/**") //拦截的路径
                    .excludePathPatterns(""); //排除的路径
        }
    
    }

    此时设置,订单页面需要的登录后,才可以访问。
    前端代码:

                <form action="maoyanmovie">
                    <div class="info">
                        <h3 th:text="${msg}"></h3>
                    </div>
                    <div class="submit">
                        <input type="submit" value="返回首页">
                    </div>
                </form>

     

    效果图:

     

     

     

     实现拦截器后,加入session,完成登录状态的保持和退出功能

    控制器代码:

    session对象主要用于属性操作和会话管理

    package com.hut.maoyanmovie.controller;
    
    import com.hut.maoyanmovie.bean.User;
    import com.hut.maoyanmovie.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    import javax.servlet.http.HttpSession;
    
    /**
     * @author HP
     * @data 2020-12-14
     */
    @Controller
    public class IoginController {
    
        @Autowired
        UserService userService;
    
        @PostMapping("loginUserByTel")
        public String login(
                @RequestParam("user_tel") String user_tel,
                @RequestParam("user_password") String user_password,
                Model model,
                HttpSession session) {
            User user = userService.loginUserByTel(user_tel, user_password);
            if (user != null) {
                model.addAttribute("msg","登录成功! 欢迎你!"+user.getUser_name());
                session.setAttribute("loginUser", user_tel);
                session.setAttribute("uid",user.getUid());
            } else {
                model.addAttribute("msg", "登录失败!用户名或密码错误!");
            }
            return "logging";
        }
    
        @RequestMapping("logout")
        public String logout(HttpSession session) {
            session.invalidate();
            return "redirect:/login";
        }
    
    
        @GetMapping("logging")
        public String logging(){
            //欢迎页面
            return "logging";
        }
    }

    session分析:

    public void setAttribute(String name,String value)设定指定名字的属性的值,并将它添加到session会话范围内,如果这个属性是会话范围内存在,则更改该属性的值。

    public Object getAttribute(String name)在会话范围内获取指定名字的属性的值,返回值类型为object,如果该属性不存在,则返回null。

    public void invalidate(),使session失效。可以立即使当前会话失效,原来会话中存储的所有对象都不能再被访问。

    此时,当输入用户密码都正确时,将用户数据通过set'方法放入session中,这样在后续查询时,可以通过判断session是否为空,得知是否登录

    在退出时,通过调用invalidate方法,失效session达到退出的效果

     

     

     

     

     

     

    完整源码

    https://github.com/MaoYanMovieWeb/maoyanmovie

     

    展开全文
  • JavaWeb应用如何实现保持登录状态

    万次阅读 多人点赞 2017-07-24 17:50:50
    做JavaWeb开发,难免会遇到登录系统保持登录状态的问题?比如说我登录过后关闭浏览器,下次再访问相同的网站,默认会显示已登录状态,一段时间内就不必再重新登录了;再比如站在后台接口设计的角度去考虑,用户登录...

    做JavaWeb开发,难免会遇到登录系统保持登录状态的问题?比如说我登录过后关闭浏览器,下次再访问相同的网站,默认会显示已登录状态,一段时间内就不必再重新登录了;再比如站在后台接口设计的角度去考虑,用户登录后,做了一系列的用户操作接口,那么这些接口不可能都带上一个userid的字段吧,这样不仅开发麻烦,而且容易被黑客攻击。那么如何解决这些问题呢?那就是通过代理服务对客户端请求的拦截来实现,经过验证后才开始执行业务逻辑。

    如何搭建代理服务的过程,这里就不详细介绍了,我们以实现保持登录状态为例,实现原理大概是这样子的:

    1.客户端请求后台登录接口。

    2.后台验证通过后,将用户的登录状态保存至cookie并写入客户端。

    3.客户端再次登录网站,请求login接口时,后台直接从客户端获取到该用户写入cookie的登录状态。

    4.通过对该状态的验证,确认用户是否需要再次登录。

    5.如cookie过期,则跳转至登录页;如未过期,则直接显示为已登录状态。

    这里前端js用ajax请求即可,如请求一个url地址:

     

    我们要注意一点,在请求成功后的Response Headers中,有Set-Cookie一项,设置一个key为java,value为myJavaData的Map值,这个是重点,有这个值客户端就会自动把这个Map值设为cookie值,这就是我们所说的登录状态loginState,如下图所示:

    那么这个Set-Cookie一项数据怎么来呢?是通过Java后台设置的,源码如下:

    // 设置格式
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST");
    response.setHeader("Access-Control-Allow-Headers","x-requested-with,content-type");
    response.setContentType("text/html;charset=utf-8");
    response.setCharacterEncoding("utf-8");
    
    // 创建Cookie
    Cookie cookie = new Cookie("java", "myJavaData");
    // 有效期,秒为单位
    cookie.setMaxAge(3600);
    // 设置cookie
    response.addCookie(cookie);
    response.getWriter().print("cookie创建成功");

    这样一来,就实现了在后台服务端为客户端设置cookie的功能,用户在下次请求相同域的接口时,java后台只需

    // 获取客户端cookie
    request.setCharacterEncoding("utf-8");
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
    	for (Cookie c : cookies) {
    		System.out.println(c.getName() + "--->" + c.getValue());
    	}
    }

    即可获取客户端的登录状态,从而做出响应操作。(注意)此处的cookie值无需客户端专门带入,会随请求自动传输到后台。上例中的setMaxAge即为登录状态的有效期设置点。

     

    还有一种业务状态是希望,用户只要关闭了浏览器,该登录状态就会清除,下次打开浏览器必须重新登录,如CSDN博客登录即使如此。这种情况我们可以使用session会话实现:java后台设置session代码

    // 设置格式
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST");
    response.setHeader("Access-Control-Allow-Headers","x-requested-with,content-type");
    response.setContentType("text/html;charset=utf-8");
    response.setCharacterEncoding("utf-8");
    
    // 使用request对象的getSession()获取session,如果session不存在则创建一个
    HttpSession session = request.getSession();
    // 将数据存储到session中
    session.setAttribute("java", "myJavaData");
    // 获取session的Id
    String sessionId = session.getId();
    // 判断session是不是新创建的
    if (session.isNew()) {
    	response.getWriter().print("session创建成功,session的id是:" + sessionId);
    } else {
    	System.out.println(session.getAttribute("java")); // 值myJavaData
    	response.getWriter().print(
    			"服务器已经存在该session了,session的id是:" + sessionId);
    }

    客户端请求后,Set-Cookie一项同样具备了值:

     

    当然,上面只是举了一些简单的例子,在实际应用中不可能只保存一个loginState这么简单,还会保存如OSS_COOKIES,使用token值验证等多种安全验证手段综合。以上实现都是在请求与页面同域的情况下,如果想要跨域实现,即客户端与后台不在同一个域名下,则通过JSONP实现。

    展开全文
  • shiro框架如何保持登录状态

    千次阅读 2021-12-17 16:44:44
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、shiro保持登录状态的方式?...现在大部分的web项目都是采用前后端分离的架构,而保持登录状态采用的最多的方式是请
  • App保持登录状态的常用方法

    千次阅读 2020-10-23 16:53:12
    如果我们的App和后端通讯采用的http通讯方式,可以利用cookie技术进行登录状态保持。比如我们可以把sessionID和有效期保存在cookie中,发给前端App,前端App收到后保存在本地。当访问后端服务把sessionID和有效期...
  • 1.前后端分离后,前端登录状态保持一般采用webstorage或是cookie来保存token或用户信息的方式来维持登录状态。如果webstorage或是cookie中没有token,则前端认为是没有登录,拦截到登录页面。vue中利用路由的before...
  • django web 登录状态保持 session

    千次阅读 2018-03-27 23:15:56
    在服务器端进行状态保持的方案就是Session。启用SessionDjango项目默认启用Session。打开test3/settings.py文件,在项MIDDLEWARE_CLASSES中启用Session中间件。禁用Session:将Session中间件删除。存储方式打开test3...
  • 爬虫中设置cookie保持登录状态

    千次阅读 2020-02-03 21:06:36
    Cookie 并不是它的原意“甜饼”的意思, 而是一个保存在客户机中的简单的文本文件, 这个文件与特定的 Web 文档关联在一起, 保存了该客户机访问这个Web 文档时的信息, 当客户机再次访问这个 Web 文档时这些信息可供该...
  • 这绝对是一件有意思的事情,做过web开发的朋友应该都做过注册登录的功能,那么测试的时候也会发现如果在同一浏览器上先登录一个用户,然后再换另一个账号登录,刷新试试看,发现原来的账号被顶掉了对吧。不要想着...
  • JWT(Json Web Token)实现状态保持

    千次阅读 2018-08-12 15:37:26
    Json web token (JWT), 是为了在网络应用间传递声明的基于JSON的开放标准,该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。其中的分布式站点的单点登陆的状态保持,在这里简单介绍下:举...
  • Cookie和Session实现保存登录状态登录

    万次阅读 多人点赞 2018-09-11 21:56:13
    首先Cookie和Session都是为了状态管理,HTTP协议是无状态的,不能保存每次提交... 对于那些需要多次提交数据才能完成的Web操作,比如登录来说,就成问题了。所以需要状态管理也就是通过Cookie和Session。 一.Cook...
  • APP保持登录状态的几种方法

    千次阅读 2018-10-29 09:39:35
    我们在使用App时,一次登录后App如果不主动退出登录或者清除数据,App会在很长一段时间内保持登录状态,或者让用户感觉到登录一次就不用每次都输入用户密码才能进行登录。银行、金融涉及到支付类的App一般不支持这种...
  • 我们一般设置cookie_lifetime为0但一关闭浏览器后session就被删除无法保持登录状态 如果设置cookie_lifetime为7200,则表示存活2个小时,此时就算关闭浏览器也不会删除session,再次打开浏览器 依然保持登录状态 ...
  • APP与服务端保持登录状态

    千次阅读 2018-07-02 17:16:39
    由于APP向服务端发起请求属于跨域访问,每次访问在服务端都会产生一个新的session,因此APP客户端与web端不同,无法通过session来保持登录状态。 为了维护app用户的登录状态,我们可以利用token来实现。 客户端...
  • JavaWeb登录状态保持解决方案

    万次阅读 2017-01-04 11:49:28
    现在需要用户的网站系统就需要登录和注册,用户登录后可以进行更多权限的操作,但是登录一次之后不能让用户每次都进行登录,需要将这个状态保存下来持久化. Session是服务器端的会话保持载体,Cookie是浏览器端...
  • 实现Web上的用户登录功能

    千次阅读 2018-11-27 13:02:40
    Web上的用户登录功能应该是最基本的功能了,可是在我看过一些站点的用户登录功能后,我觉得很有必要写一篇文章教大家怎么来做用户登录功能。下面的文章告诉大家这个功能可能并没有你所想像的那么简单,这是一个关系...
  • 几种保持登录状态的方式

    千次阅读 2018-09-08 14:57:15
    几种保持登录状态的方式 白话讲session 这是我的博客目录,欢迎阅读 三种保持会话的方式 (一)session机制保持会话 存在的问题 高并发情况下,会占用服务器大量内存 分布式(一个业务分成几个子业务,部署在多个...
  • 实现登录状态保持的方法 方法一:cookie和session配合使用 首先,用户登录输入用户名和密码,浏览器发送post请求,服务器后台获取用户信息,查询数据库验证用户信息是否正确。如果验证通过,就会创建session来存储...
  • 登录状态的保存Session

    千次阅读 2019-10-02 11:20:17
    登录状态的保存Session Session是什么 Session一般译作会话,牛津词典对其的解释是进行某活动连续的一段时间。从不同的层面看待session,它有着类似但不全然相同的含义。比如,在web应用的用户看来,他打开浏览器...
  • Web登录其实没那么简单

    千次阅读 2021-05-27 01:01:33
    我们可以在WEB端对之前案例中提到的username+MD5(password)+token通过签名,得到一个字段checkCode,并将checkCode发送给服务器,服务器根据用户发送的checkCode以及自身对原始数据签名进行运算比对,从而确认数据...
  • 由于是软件公司,项目用户量很小,而且是传统项目,所以用session来存储用户的登录状态。前端是移动端,我为session对象写了一个工具类,供自己用,记录一下,说不定以后还会用到。 先上session工具的代码: ...
  • System.Net.WebException: 基础连接已经关闭: 服务器关闭了本应保持活动状态的连接。 ---> System.IO.IOException: 无法从传输连接中读取数据: 您的主机中的软件中止了一个已建立的连接。。 ---> System.Net.Sockets...
  • , response_type 是 填code, scope 是 应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即可 ,state 否 用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 297,241
精华内容 118,896
关键字:

web保持登录状态