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

}

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容