shiro登录密码验证以及登录状态验证流程

登录密码验证流程

1.入口

  • 获取前端传过来的用户信息User
  • 把用户信息User传入到实例对象UsernamePasswordToken
UsernamePasswordToken token = new UsernamePasswordToken("zhangsan","666");
//UsernamePasswordToken 的属性
// getUsername 账户,账号
// getPassword 密码
// getPrincipal 身份 
// getCredentials 加密凭据
// getHost 主机地址
// isRememberMe 是否记住账号密码
  • 调用subject的login方法开始进行登录验证
 Subject subject = SecurityUtils.getSubject();
//此处传的token是实例化后的对象UsernamePasswordToken
 subject.login(token);

2.密码匹配校验

  • 创建自定义的relam类继承 AuthorizingRealm 实现doGetAuthenticationInfo 方法和doGetAuthorizationInfo方法
  • 进入到 自定义的relam里的doGetAuthenticationInfo方法里边进行获取传过来的用户信息,然后进行密码匹配,
  • 随后按照密码的加密方式选择密码匹配器,自定义的话 选择对应的继承就好了


    密码匹配规则类

doGetAuthorizationInfo

这个方法主要用来校验用户的权限,那什么时候会进入这个方法?
  • subject.hasRole(“admin”) 或 subject.isPermitted(“admin”):自己去调用这个是否有什么角色或者是否有什么权限的时候;
  • @RequiresRoles("admin") :在方法上加注解的时候;
  • [@shiro.hasPermission name = "admin"][/@shiro.hasPermission]:在页面上加shiro标签的时候,即进这个页面的时候扫描到有这个标签的时候
    ------之后生成session

登录状态验证流程

1. 入口

  • 所有的登录以及未登录的请求都先进入到filter中,按照filter的层级来进行层层校验URL地址
  • isAccessAllowed 所有的接口都会先进入此方法,如果此接口返回true,就判定为已登陆,如果返回false就判定为未登录 然后进入到onAccessDenied方法中 isAccessAllowed 方法的mappedValue 是登录成功的用户的权限信息,可以用来判断此用户拥有那些权限
  • onAccessDenied 判定此用户未登录是否可以访问此页面或者接口
package com.eat.config;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @version V1.0
 * @Package com.eat.config
 * @ClassName MyFilter
 * @Description 自定义shiro的过滤器
 * @Author 王振鹏
 * @date 2020/10/26 23:39
 **/
public class MyFilter extends AuthenticatingFilter {

    /**
     * @Summar
     * @Param: [request, response]
     * @Return: org.apache.shiro.authc.AuthenticationToken
     * @Author: TheRaging
     * @Date: 2020/10/26 23:46
     * @Description TODO 创建Token
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        System.out.println("进到了创建TOKEN的方法中");

        return null;
    }

    /**
     * @Summary
     * @Param: [request, response, mappedValue]
     * @Return: boolean
     * @Author: TheRaging
     * @Date: 2020/10/26 23:43
     * @Description 判断是否登录,主要用于权限校验,     false的话就代表未登录,直接进入onAccessDenied方法,
     * 如果为true就代表已经登录过,然后直接访问控制器
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {

       /*
        //权限访问逻辑
        Subject subject = getSubject(request, response);
        String[] rolesArray = (String[]) mappedValue;
        //没有权限访问
        if (rolesArray == null || rolesArray.length == 0) {
            return true;
        }
        for (int i = 0; i < rolesArray.length; i++) {
            //若当前用户是rolesArray中的任何一个,则有权限访问
            if (subject.hasRole(rolesArray[i])) {
                return true;
            }
        }*/
        ShiroHttpServletRequest rrr = (ShiroHttpServletRequest)request;
        String s = rrr.getRequestURI();
        System.out.println(s);
        String token =  getRequestToken((HttpServletRequest)request);
        if(StringUtils.isEmpty(token)){
            //没有token的话 就去登录
            System.out.println("tojkren-----------"+token);
            return  false;
        }else {
            //有token的话 选哟解析验证token 验证通过的话就直接权限校验或者直接访问,如果校验不通过的话就拒绝登录
            System.out.println("获取到了TOKEN-----------"+token);
            return  true;
        }






    }

    /**
     * @Summary
     * @Param: [request, response]
     * @Return: boolean
     * @Author: TheRaging
     * @Date: 2020/10/26 23:44
     * @Description 未登录状态进入此方法,
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {



        System.out.println("进入到了onAccessDenied方法中");
        return false;
    }

    /**
     * @Summary
     * @Param: [token, e, request, response]
     * @Return: boolean
     * @Author: TheRaging
     * @Date: 2020/10/26 23:46
     * @Description TODO  登录失败的一个处理
     */
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        System.out.println("进入到了onLoginFailure方法中");
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        try {
            //处理登录失败的异常
            Throwable throwable = e.getCause() == null ? e : e.getCause();
           /* R r = R.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());

            String json = new Gson().toJson(r);
            httpResponse.getWriter().print(json);*/
            httpResponse.getWriter().print("2123");
        } catch (IOException e1) {

        }
        return false;
    }

    /**
     * @Summary
     * @Param: [token, subject, request, response]
     * @Return: boolean
     * @Author: TheRaging
     * @Date: 2020/10/26 23:47
     * @Description TODO  登录成功的一个处理
     */
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token,
                                     Subject subject, ServletRequest request, ServletResponse response)
            throws Exception {
        System.out.println("进入到了onLoginSuccess方法中");
        Session session = SecurityUtils.getSubject().getSession();
        /*UserInfo user = (UserInfo) subject.getPrincipal();
        session.setAttribute("userName", user.getUserName());
        session.setAttribute("userId", user.getUserId());*/
        issueSuccessRedirect(request, response);
        return false;
    }
    /**
     * 获取请求的token
     */
    private String getRequestToken(HttpServletRequest httpRequest){
        //从header中获取token
        String token = httpRequest.getHeader("token");

        //如果header中不存在token,则从参数中获取token
        if(StringUtils.isBlank(token)){
            token = httpRequest.getParameter("token");
        }

        return token;
    }
}

shiro的配置文件

package com.eat.config;



import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
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.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.util.HashMap;
import java.util.Map;
/**
 * @version V1.0
 * @Package com.eat.config
 * @ClassName ShiroConfig
 * @Description TODO
 * @Author 王振鹏
 * @date 2020/10/23 22:22
 **/
@Configuration
public class ShiroConfig {

    /**
     * @Summary
     * @Param: []
     * @Return: CustomRealm
     * @Author: TheRaging
     * @Date: 2020/10/23 22:28
     * @Description 配置ShiroRealm用于登录验证逻辑,返回自定义的ShiroRealm 相当于一个数据源
     * 身份验证(getAuthenticationInfo 方法)验证账户和密码,并返回相关信息
     * 权限获取(getAuthorizationInfo 方法) 获取指定身份的权限,并返回相关信息
     * 令牌支持(supports方法)判断该令牌(Token)是否被支持
     */
    @Bean
    public MyRealm myShiroRealm() {
        MyRealm customRealm = new MyRealm();
        //自定义密码验证
        customRealm.setCredentialsMatcher(myCredentialsMatcher());
        return customRealm;
    }

    /**
     * @Summary
     * @Param: []
     * @Return: java.lang.SecurityManager
     * @Author: TheRaging
     * @Date: 2020/10/23 22:29
     * @Description 配置SecurityManager,用于管理认证
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        //设置自定义的session管理器
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * @Summary
     * @Param: [securityManager]
     * @Return: ShiroFilterFactoryBean
     * @Author: TheRaging
     * @Date: 2020/10/23 22:30
     * @Description 配置ShiroFilterFactoryBean过滤器,用于过滤url地址,然后决定哪些路径需要认证
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置使用自定义的filter进行过滤
        Map<String, Filter> filters = new HashMap<>();
        filters.put("filter", new MyFilter());
        //配置自定义的过滤规则
        shiroFilterFactoryBean.setFilters(filters);
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> map = new HashMap<>();
        //登录
        map.put("/test/login", "anon");
        //登出
        map.put("/logout", "logout");
        //登录失败跳转的页面
       // shiroFilterFactoryBean.setLoginUrl("/test/login");
        //配置登录成功的跳转页面,默认跳转到/
        //shiroFilterFactoryBean.setSuccessUrl("/index");
        //没有权限,权限校验失败跳转的页面
        //shiroFilterFactoryBean.setUnauthorizedUrl("/error");
        //对所有用户认证
        map.put("/**", "filter");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    /**
     * shiro缓存管理器
     * 1 添加相关的maven支持
     * 2 注册这个bean,将缓存的配置文件导入
     * 3 在securityManager 中注册缓存管理器,之后就不会每次都会去查询数据库了,相关的权限和角色会保存在缓存中,但需要注意一点,更新了权限等操作之后,需要及时的清理缓存
     */
   /* @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager cacheManager = new EhCacheManager();
        cacheManager.setCacheManagerConfigFile("classpath:config/ehcache.xml");
        return cacheManager;
    }*/


    /**
     * 自定义的 shiro session 缓存管理器,用于跨域等情况下使用 token 进行验证,不依赖于sessionId
     * @return
     */
    @Bean
    public SessionManager sessionManager(){
        //将我们继承后重写的shiro session 注册
        MySession shiroSession = new MySession();
        //如果后续考虑多tomcat部署应用,可以使用shiro-redis开源插件来做session 的控制,或者nginx 的负载均衡
        shiroSession.setSessionDAO(new EnterpriseCacheSessionDAO());
        return shiroSession;
    }

    /**
     * @Summary
     * @Param: []
     * @Return: MyCredentialsMatcher
     * @Author: TheRaging
     * @Date: 2020/10/23 23:45
     * @Description 配置自定义的密码校验类MyCredentialsMatcher
     * MyCredentialsMatcher 需要继承CredentialsMatcher 实现重写
     * 但是同时需要在SecurityManager中设置
     * SecurityManager.setCredentialsMatcher(myCredentialsMatcher());
     */
    @Bean
    public MyCredentialsMatcher myCredentialsMatcher() {
        return new MyCredentialsMatcher();
    }


    /**
     * @Summary
     * @Param: []
     * @Return: org.apache.shiro.mgt.SecurityManager
     * @Author: TheRaging
     * @Date: 2020/10/23 23:51
     * @Description 配置实现自定义的securityManager
     * MySessionManager需要继承 DefaultWebSessionManager 实现重写
     * 但是同时也需要在 securityManager 中添加
     * SecurityManager.setSessionManager(sessionManager());
     */
  /*  @Bean
    public SessionManager sessionManager(){
        MySessionManager mySessionManager = new MySessionManager();
        //这里可以不设置。Shiro有默认的session管理。如果缓存为Redis则需改用Redis的管理
        mySessionManager.setSessionDAO(new EnterpriseCacheSessionDAO());
        return mySessionManager;
    }*/



    //DefaultAdvisorAutoProxyCreator 和AuthorizationAttributeSourceAdvisor两个在一起才能支Shiro的注解权限控制实现
    /**
     * @Summary
     * @Param: []
     * @Return: org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator
     * @Author: TheRaging
     * @Date: 2020/10/23 22:33
     * @Description  配置,这个是AOP的,相当于一个切面
     *  他会扫描所有的类中的Advisor,
     *  然后这些Advisor应用到所有符合切入点的Bean中
     */
    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    /**
     * @Summary
     * @Param: [securityManager]
     * @Return: AuthorizationAttributeSourceAdvisor
     * @Author: TheRaging
     * @Date: 2020/10/23 22:38
     * @Description 配置 aop  这个类就相当于切点了
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容