spring boot + shiro 实现登陆 踢出用户功能 (挤人) 以及UnknownSessionException异常问题 记住我功能

本文介绍了如何使用Spring Boot和Shiro实现限制同一账号只能一个用户登录并挤出已登录用户的功能,同时解决了UnknownSessionException异常问题。详细步骤包括配置pom、ShiroConfigBean、ShiroRealm、SessionDAO、KickoutSessionControlFilter,以及服务层和服务控制器的设置。此外,还提供了Remember Me功能的提示。

简介:踢出用户功能:就是限制一个账号登陆人数。

                        本文限定一个账号一个用户登陆,并且是挤掉前一个用户


目录

 

首先 pom

然后Shiro配置Bean  ShiroConfigBean

然后配置 ShiroRealm(百度翻译: Realm 领域)

然后sessiondao SessionDAO

然后 配置踢人(挤人)逻辑 KickoutSessionControlFilter

UnknownSessionException异常原因

service

@RestController


首先 pom

        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.2.2</version>
        </dependency>

然后Shiro配置Bean  ShiroConfigBean


import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
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.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author liguanhua
 * @Date: 2019/12/17 11:21
 * @Description: Shiro配置Bean
 */
@Configuration
public class ShiroConfigBean {
    private static final String NOT_FILTER_STR = "/login|/photoUpload|/img/|/loginOut|/api/login|/importExcel|/api/deviceGis/add" +
            "|/api/log/uploadLog|/upload|/api/app/update|/api/client/update|/api/device/deviceDataByMDCodeL|/land";
    private static List<String> NOTFILTER_ARRAY;

    static {
        NOTFILTER_ARRAY = Arrays.asList(NOT_FILTER_STR.split("\\|"));
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(org.apache.shiro.mgt.SecurityManager securityManager) {
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //自定义拦截器 ---------------配置挤人功能呢---------------------- 
        Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
//        限制同一帐号同时在线的个数。
        filtersMap.put("kickout", kickoutSessionControlFilter());
        shiroFilterFactoryBean.setFilters(filtersMap);
//        ----------------------------------------------------

        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 设置login URL
        shiroFilterFactoryBean.setLoginUrl("/land");
        for (String str : NOTFILTER_ARRAY) {
            shiroFilterFactoryBean.setLoginUrl(str);
        }
        // 设置不需要校验的 url
        for (String str : NOTFILTER_ARRAY) {
            filterChainDefinitionMap.put(str, "anon");
        }
        filterChainDefinitionMap.put("/Exception.class", "anon");
        // 我写的url一般都是xxx.action,根据你的情况自己修改
        filterChainDefinitionMap.put("/*", "authc");
        // 退出系统的过滤器
        filterChainDefinitionMap.put("/loginOut", "logout");
        // 最后一班都,固定格式
        //其他资源都需要认证  authc 表示需要认证才能进行访问 user表示配置记住我或认证通过可以访问的地址 kickout 挤人功能配置
        filterChainDefinitionMap.put("/**", "kickout,authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /*
     * 凭证匹配器 (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     * 所以我们需要修改下doGetAuthenticationInfo中的代码; )
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1);// 散列的次数,比如散列两次,相当于md5(md5(""));
        return hashedCredentialsMatcher;
    }
//    配置 shiroRealm
    @Bean
    public ShiroRealm shiroRealm() {
        ShiroRealm myShiroRealm = new ShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    @Bean
    public org.apache.shiro.mgt.SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入自定义的realm;
        securityManager.setRealm(shiroRealm());
        // 注入缓存管理器;
        securityManager.setCacheManager(ehCacheManager());
        //自定义session管理     添加自定义session管理,解决 异常 sessionId(无效)冲突问题。 挤人功能所需配置
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /*
     * 开启shiro aop注解支持 使用代理方式;所以需要开启代码支持;
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            org.apache.shiro.mgt.SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * DefaultAdvisorAutoProxyCreator,Spring的一个bean,由Advisor决定对哪些类的方法进行AOP代理。
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    /*
     * shiro缓存管理器;
     * 需要注入对应的其它的实体类中-->安全管理器:securityManager可见securityManager是整个shiro的核心;
     */
    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager ehcache = new EhCacheManager();
        CacheManager cacheManager = CacheManager.getCacheManager("shiro");
        if(cacheManager == null){
            try {
                cacheManager = CacheManager.create(ResourceUtils.getInputStreamForPath("classpath:ehcache.xml"));
            } catch (CacheException | IOException e) {
                e.printStackTrace();
            }
        }
        ehcache.setCacheManager(cacheManager);
        return ehcache;
    }

    /**
     * 限制同一账号登录同时登录人数控制
     *
     * @return
     */
    public KickoutSessionControlFilter kickoutSessionControlFilter() {
        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
        //用于根据会话ID,获取会话进行踢出操作的;
        kickoutSessionControlFilter.setSessionManager(sessionManager());
        //使用cacheManager获取相应的cache来缓存用户登录的会话;用于保存用户—会话之间的关系的;
        kickoutSessionControlFilter.setCacheManager(ehCacheManager());
        //是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;
        kickoutSessionControlFilter.setKickoutAfter(false);
        //同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;
        kickoutSessionControlFilter.setMaxSession(1);
        //被踢出后重定向到的地址;
        kickoutSessionControlFilter.setKickoutUrl("/land?kickout=1");
        return kickoutSessionControlFilter;
    }

    /**
     * EnterpriseCacheSessionDAO shiro sessionDao层的实现;
     * 提供了缓存功能的会话维护,默认情况下使用MapCache实现,内部使用ConcurrentHashMap保存缓存的会话。
     */
    @Bean
    public EnterpriseCacheSessionDAO enterCacheSessionDAO() {
        EnterpriseCacheSessionDAO enterCacheSessionDAO = new EnterpriseCacheSessionDAO();
        //添加缓存管理器
        //添加ehcache活跃缓存名称(必须和ehcache缓存名称一致)
        enterCacheSessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
        return enterCacheSessionDAO;
    }

    @Bean
    public SimpleCookie sessionIdCookie() {
        //DefaultSecurityManager
        SimpleCookie simpleCookie = new SimpleCookie();
        //如果在Cookie中设置了"HttpOnly"属性,那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息,这样能有效的防止XSS攻击。
        simpleCookie.setHttpOnly(true);
//        用于解决sessionID冲突问题。换个名字
        simpleCookie.setName("SHRIOSESSIONID");
        //单位秒
        simpleCookie.setMaxAge(86400);
        return simpleCookie;
    }

    /**
     * @描述:sessionManager添加session缓存操作DAO
     * @return
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(enterCacheSessionDAO());
        sessionManager.setSessionIdCookie(sessionIdCookie());
        return sessionManager;
    }
    
}

然后配置 ShiroRealm(百度翻译: Realm 领域)

import com.wulianwang.manage.model.dbentity.system.UserInfo;
import com.wulianwang.manage.service.system.impl.UserService;
import com.wulianwang.manage.utils.util.Utils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.Set;

import static com.wulianwang.manage.utils.base.UserUtil.setUser;

/**
 * @author liguanhua
 * @Date: 2019/12/17 11:23
 * @Description:
 */
public class ShiroRealm extends AuthorizingRealm {
    /**
     * 方面用于加密 参数:AuthenticationToken是从表单穿过来封装好的对象
     */
    @Autowired
    private UserService userService;
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("doGetAuthenticationInfo:" + token);

        // 将AuthenticationToken强转为AuthenticationToken对象
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;

        // 获得从表单传过来的用户名
        String userName = upToken.getUsername();
        UserInfo user = userService.selectUserByUserName(userName);
        // 如果用户不存在,抛此异常
        if (!Utils.isNotEmpty(user)) {
            throw new UnknownAccountException("无此用户名!");
        }

        // 认证的实体信息,可以是username,也可以是用户的实体类对象,这里用的用户名
        Object principal = userName;
        // 从数据库中查询的密码
        Object credentials = user.getPassword();

        // 当前realm对象的名称,调用分类的getName()
        String realmName = this.getName();

        // 创建SimpleAuthenticationInfo对象,并且把username和password等信息封装到里面
        // 用户密码的比对是Shiro帮我们完成的
        SimpleAuthenticationInfo info = null;
        info = new SimpleAuthenticationInfo(principal, credentials, realmName);
        setUser(user);
        return info;
    }

    // 用于授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        System.out.println("MyShiroRealm的doGetAuthorizationInfo授权方法执行");

        // User user=(User)
        // principals.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户
        // System.out.println("在MyShiroRealm中AuthorizationInfo(授权)方法中从session中获取的user对象:"+user);

        // 从PrincipalCollection中获得用户信息
        Object principal = principals.getPrimaryPrincipal();
        System.out.println("ShiroRealm  AuthorizationInfo:" + principal.toString());

        // 根据用户名来查询数据库赋予用户角色,权限(查数据库)
        Set<String> roles = new HashSet<>();
        Set<String> permissions = new HashSet<>();
//		2018.09.14更新
        //		给用户添加user权限 (没有进行判断、对所有的用户给user权限)
//        if("user".equals(principal)){
//            roles.add("user");
//            permissions.add("user:query");
//        }
////		当用户名为admin时 为用户添加权限admin  两个admin可以理解为连个字段
//        if ("admin".equals(principal)) {
//            roles.add("admin");
//            permissions.add("admin:query");
//        }
////		为用户添加visit游客权限,在url中没有为visit权限,所以,所有的操作都没权限
//        if("visit".equals(principal)){
//            roles.add("visit");
//            permissions.add("visit:query");
//        }
//              更新以上代码
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
        //添加权限
        info.setStringPermissions(permissions);
        return info;
        // return null;
    }
}

然后sessiondao SessionDAO

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;

import java.io.Serializable;
import java.util.Collection;

/**
 * @author liguanhua
 * @Date: 2019/12/19 10:36
 * @Description:
 */
public interface SessionDAO {
    /*如DefaultSessionManager在创建完session后会调用该方法;
      如保存到关系数据库/文件系统/NoSQL数据库;即可以实现会话的持久化;
      返回会话ID;主要此处返回的ID.equals(session.getId());
    */
    Serializable create(Session session);

    //根据会话ID获取会话
    Session readSession(Serializable sessionId) throws UnknownSessionException;

    //更新会话;如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
    void update(Session session) throws UnknownSessionException;

    //删除会话;当会话过期/会话停止(如用户退出时)会调用
    void delete(Session session);

    //获取当前所有活跃用户,如果用户量多此方法影响性能
    Collection<Session> getActiveSessions();

}

然后 ehcache.xml配置 boot 2.0以上应该是需要加 defaultCache 配置。如果你报错提示 default 错误可以加上这个

<ehcache>
    <diskStore path="java.io.tmpdir"/>
    <!-- name 缓存名称 -->
    <!-- maxElementsInMemory 内存中最大缓存对象数,看着自己的heap大小来搞 -->
    <!-- eternal:true表示对象永不过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false -->
    <!-- maxElementsOnDisk:硬盘中最大缓存对象数,若是0表示无穷大 -->
    <!-- overflowToDisk:true表示当内存缓存的对象数目达到了maxElementsInMemory界限后,
    会把溢出的对象写到硬盘缓存中。注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行。-->
    <!-- diskSpoolBufferSizeMB:磁盘缓存区大小,默认为30MB。每个Cache都应该有自己的一个缓存区。-->
    <!-- diskPersistent:是否缓存虚拟机重启期数据  -->
    <!-- diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认为120秒 -->

    <!--timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。当对象自从最近一次被访问后,
    如果处于空闲状态的时间超过了timeToIdleSeconds属性值,这个对象就会过期,
    EHCache将把它从缓存中清空。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。-->

    <!--timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。
    如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期,
    EHCache将把它从缓存中清除。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。-->
    <!-- clearOnFlush:内存数量最大时是否清除 -->
    <!-- memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,
    Ehcache将会根据指定的策略去清理内存。可选策略有:LRU(最近最少使用,默认策略)、
    FIFO(先进先出)、LFU(最少访问次数)。--><!-- shiro-activeSessionCache活跃用户session缓存策略 -->
    <defaultCache
            maxElementsInMemory="10000"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <!--<persistence strategy="localTempSwap"/>-->
    </defaultCache>
    <cache name="shiro-activeSessionCache"
           maxElementsInMemory="10000"
           timeToIdleSeconds="86400"
           timeToLiveSeconds="86400"
           maxElementsOnDisk="10000000"
           diskExpiryThreadIntervalSeconds="120"
           memoryStoreEvictionPolicy="LRU">
        <!-- <persistence strategy="localTempSwap"/>-->
    </cache>
    <!-- 登录记录缓存 锁定2分钟 -->
    <cache name="passwordRetryCache"
           maxEntriesLocalHeap="10000"
           eternal="false"
           timeToIdleSeconds="120"
           timeToLiveSeconds="0"
           overflowToDisk="false"
           statistics="false">
    </cache>
</ehcache>

然后 配置踢人(挤人)逻辑 KickoutSessionControlFilter


import java.io.Serializable;
import java.util.ArrayDeque;
import java.util.Deque;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author liguanhua
 * @Date: 2019/12/18 14:32
 * @Description: 挤人逻辑
 */
public class KickoutSessionControlFilter extends AccessControlFilter {
    private static final Logger logger = LoggerFactory
            .getLogger(KickoutSessionControlFilter.class);
    private static SessionDAO sessionDAO;
    private String kickoutUrl; // 踢出后到的地址
    private boolean kickoutAfter = false; // 踢出之前登录的/之后登录的用户 默认false踢出之前登录的用户
    private int maxSession = 1; // 同一个帐号最大会话数 默认1
    private SessionManager sessionManager;
    private Cache<String, Deque<Serializable>> cache;

    public void setKickoutUrl(String kickoutUrl) {
        this.kickoutUrl = kickoutUrl;
    }

    public void setKickoutAfter(boolean kickoutAfter) {
        this.kickoutAfter = kickoutAfter;
    }

    public void setMaxSession(int maxSession) {
        this.maxSession = maxSession;
    }

    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    // 设置Cache的key的前缀
    public void setCacheManager(CacheManager cacheManager) {
        //必须和ehcache缓存配置中的缓存name一致
        this.cache = cacheManager.getCache("shiro-activeSessionCache");
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request,
                                      ServletResponse response, Object mappedValue) throws Exception {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request,
                                     ServletResponse response) throws Exception {
        Subject subject = getSubject(request, response);
        // 没有登录授权 且没有记住我
        if (!subject.isAuthenticated() && !subject.isRemembered()) {
            // 如果没有登录,直接进行之后的流程
            return true;
        }
        Session session = subject.getSession();
        logger.debug("==session时间设置:" + String.valueOf(session.getTimeout())
                + "===========");
        try {
            // 当前用户
            String username = subject.getPrincipal() + "";
            logger.debug("===当前用户username:==" + username);
            Serializable sessionId = session.getId();
            logger.debug("===当前用户sessionId:==" + sessionId);
            // 读取缓存用户 没有就存入
            Deque<Serializable> deque = cache.get(username);
            logger.debug("===当前deque:==" + deque);
            if (deque == null) {
                // 初始化队列
                deque = new ArrayDeque<Serializable>();
            }
            // 如果队列里没有此sessionId,且用户没有被踢出;放入队列
            if (!deque.contains(sessionId)
                    && session.getAttribute("kickout") == null) {
                // 将sessionId存入队列
                deque.push(sessionId);
                // 将用户的sessionId队列缓存
                cache.put(username, deque);
            }
            // 如果队列里的sessionId数超出最大会话数,开始踢人
            while (deque.size() > maxSession) {
                logger.debug("===deque队列长度:==" + deque.size());
                Serializable kickoutSessionId = null;
                // 是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;
                if (kickoutAfter) { // 如果踢出后者
                    kickoutSessionId = deque.removeFirst();
                } else { // 否则踢出前者
                    kickoutSessionId = deque.removeLast();
                }
                // 踢出后再更新下缓存队列
                cache.put(username, deque);
                try {
                    // 获取被踢出的sessionId的session对象
                    Session kickoutSession = sessionManager
                            .getSession(new DefaultSessionKey(kickoutSessionId));
                    if (kickoutSession != null) {
                        // 设置会话的kickout属性表示踢出了
                        kickoutSession.setAttribute("kickout", true);
                    }
                } catch (Exception e) {// ignore exception
                    e.printStackTrace();
                }
            }

            // 如果被踢出了,(前者或后者)直接退出,重定向到踢出后的地址
            if ((Boolean) session.getAttribute("kickout") != null
                    && (Boolean) session.getAttribute("kickout") == true) {
                // 会话被踢出了
                try {
                    // 退出登录
                    subject.logout();
                } catch (Exception e) { // ignore
                }
                saveRequest(request);
                logger.debug("==踢出后用户重定向的路径kickoutUrl:" + kickoutUrl);
                // 重定向
                WebUtils.issueRedirect(request, response, kickoutUrl);
                return false;
            }
            return true;
        } catch (Exception e) { // ignore
            //重定向到登录界面
            WebUtils.issueRedirect(request, response, "/land");
            return false;
        }
    }
}

UnknownSessionException异常原因

只所以出现这个问题是因为在shiro的DefaultWebSessionManager类中,默认Cookie名称是JSESSIONID,这样的话与servlet容器名冲突, 如jetty, tomcat等默认JSESSIONID, 当跳出shiro servlet时如error-page容器会为JSESSIONID重新分配值导致登录会话丢失!

我们只需要自己指定一个与项目运行容器不冲突的sessionID就好了

上面配置sessionManager(sessionIdCookie)就是为了解决这个问题。使用了SHRIOSESSIONID

service

UserService

    /**
             * 根据用户名获取用户信息
             * @param userName
            * @return
          */
          public UserInfo selectUserByUserName(String userName){
              UserInfo user = userMapper.selectUserByUserName(userName);
              return user;
           }

@RestController

@PostMapping(value = "/login")
    public Message userLogin(@RequestParam Map<String, String> param, HttpSession session) {
        // 获得当前Subject
        Subject currentUser = SecurityUtils.getSubject();
        String userName = param.get("userName");
        String password = param.get("password");
        // 验证用户是否验证,即是否登录
//        if (!currentUser.isAuthenticated()) {
        String msg = "";
        // 把用户名和密码封装为 UsernamePasswordToken 对象
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);

        // remembermMe记住密码 关闭浏览器后再次打开浏览器。系统将Cookie 存到本地。可以不用登陆直接进入
        token.setRememberMe(true);
        try {
            // 执行登录.
            currentUser.login(token);
            return renderSuccess(map);
        } catch (IncorrectCredentialsException e) {
            return renderError(Global.NAME_OR_PWD_ERROR);
        } catch (ExcessiveAttemptsException e) {
            msg = "登录失败次数过多";
            return renderError(msg);
        } catch (LockedAccountException e) {
            msg = "帐号已被锁定";
            return renderError(msg);
        } catch (DisabledAccountException e) {
            return renderError(Global.DISABLED_MESSAGE);
        } catch (ExpiredCredentialsException e) {
            msg = "帐号已过期";
            return renderError(msg);
        } catch (UnknownAccountException e) {
            msg = "帐号不存在";
            return renderError(msg);
        } catch (UnauthorizedException e) {
            msg = "您没有得到相应的授权";
            return renderError(msg);
        } catch (Exception e) {
            return renderError(msg);
        }
//        }
//        return renderSuccess("您已登陆");
    }

 记住我

其他详细权限等介绍可看 https://blog.youkuaiyun.com/qq_32786139/article/details/82658197

评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值