shiro登录拦截

1. shiro介绍

shiro的程序运行流程图:



Application code:应用程序代码,开发人员编写的代码
Subject:主体,当前用户
SecurityManager:安全管理器,shiro框架的核心对象,管理各个组件
Realm:类似于Dao,负责访问安全数据(用户数据、角色数据、权限数据)

2.shiro的表单认证流程(可以自定义表单认证)

参考http://blog.csdn.net/zcl_love_wx/article/details/51577058


实际中可能会有点差别,灵活处理

1.已经登录,放行
3.未登录,判断是否是登录请求,不是则跳转到登录页面,并保存当前请求
5.是登录请求,并且为get请求,则直接跳转到loginUrl,即登录页面
6.是post请求,说明从登录页面提交了表单,开始表单,realm验证
7.验证失败,则将错误信息存入shiroLoginFailure中,并重新跳转到登录页面,并显示错误信息
8.验证成功则跳转到之前保存的请求,没有则跳转到successUrl指定页面

注意:登录请求/login被拦截进行验证,最后还是会跳到/login的Controller中

3.实际应用到项目中

  1. 导入相关的包,shiro-all.jar
  2. 在web.xml文件中配置Spring用于整合shiro的过滤器
   <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <async-supported>true</async-supported>
        <init-param>
            <param-name>targetFilterLifecycle</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  1. 在Spring-mvc中配置一个bean,id和之前过滤器的名称相同
<!--shiro配置 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/login.html" />        <!--需要登录的请求在未登录的情况下会被拦截到这个地址 -->
        <!-- <property name="successUrl" value="/system/index" /> --> 
        <property name="unauthorizedUrl" value="/unauthorized" />
        <property name="filters">
            <util:map>
                <entry key="logout" value-ref="logoutFilter"/>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </util:map>

        </property>
        <property name="filterChainDefinitions">
            <value>
                /assets/** = anon
                /*.ico = anon
                /nirvana/** = anon
                /login = authc
                /logout = logout
                /** = authc
            </value>
        </property>
    </bean>

具体细节就不再描述了,请百度自行了解

  1. 根据shiro的介绍,配置安全管理器securityManager,及其依赖的相关属性
    <!--配置权限核心管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="userRealm" />
        <property name="cacheManager" ref="cacheManager" />
        <property name="sessionManager" ref="sessionManager"></property>
    </bean>

    <!--替换默认的form 验证过滤器 -->
    <bean id="formAuthenticationFilter"
          class="com.sf.tst.web.shiro.filter.CustomFormAuthenticationFilter">
        <property name="loginUrl" value="/login"/> 
        <!--表单上的用户名/密码 下次自动登录的参数名 -->
        <property name="usernameParam" value="userid"/>
        <property name="passwordParam" value="password"/>
        <!--<property name="rememberMeParam" value="rememberMe"/> -->
    </bean>

    <!-- 退出登录过滤器 -->
    <bean id="logoutFilter" class="com.sf.tst.web.shiro.filter.CoustomLogoutFilter">
        <!--重定向 -->
        <property name="redirectUrl" value="/login" />
    </bean>

    <!-- 自定义realm -->  
    <bean id="userRealm" class="com.sf.tst.web.shiro.realm.UserRealm">  
        <!-- 将凭证匹配器设置到realm中,realm按照凭证匹配器的要求进行散列 -->  
        <!-- <property name="credentialsMatcher" ref="credentialsMatcher" /> -->  
    </bean>

    <!--session管理器,设置过期时间等属性-->
     <bean id="sessionManager"
          class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="deleteInvalidSessions" value="true"></property>
        <property name="sessionValidationInterval" value="1800000"></property>
        <property name="globalSessionTimeout" value="1800000"></property>
        <property name="cacheManager" ref="cacheManager"></property>
    </bean>

    <!--默认缓存管理器 -->
    <bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />

这里自定义了表单过滤器和登出过滤器

  1. 自定义表单过滤器CustomFormAuthenticationFilter
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
   // 创建 Token
    protected CaptchaUsernamePasswordToken createToken(
            ServletRequest request, ServletResponse response) {

        String username = getUsername(request);
        String password = getPassword(request);
        String captcha = getCaptcha(request);
        boolean rememberMe = isRememberMe(request);
        return new CaptchaUsernamePasswordToken(username, password.toCharArray(), rememberMe, captcha);
    }



    // 认证
    protected boolean executeLogin(ServletRequest request,
                                   ServletResponse response) throws Exception {
        CaptchaUsernamePasswordToken token = createToken(request, response);

        try {
           /* doCaptchaValidate((HttpServletRequest) request, token);*/

            Subject subject = getSubject(request, response);
            subject.login(token);

            return onLoginSuccess(token, subject, request, response);
        } catch (AuthenticationException e) {
            return onLoginFailure(token, e, request, response);
        }
    }
}
  1. 自定义登出过滤器
public class CoustomLogoutFilter extends LogoutFilter {

    private static final Logger log = LoggerFactory.getLogger(CoustomLogoutFilter.class);

    public CoustomLogoutFilter() {}

    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        Subject subject = getSubject(request, response);
        String redirectUrl = getRedirectUrl(request, response, subject);
        try {
            subject.logout();
        } catch (SessionException ise) {
            log.debug("Encountered session exception during logout.  This can generally safely be ignored.", ise);
        }

        issueRedirect(request, response, redirectUrl);
        return false;
    }
}
  1. 自定义BosRealm
public class UserRealm extends AuthorizingRealm{
   private static Logger log = LoggerFactory.getLogger(UserRealm.class);
    
    @Autowired
    SysUserInfoServiceImpl userService;
   /**
     * 登录认证
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        log.info("登录认证");
        // 通过表单接收的用户名
        String username = token.getUsername();
        String password = token.getPassword().toString();
        if (token.getPassword() != null) {
            password = new String(token.getPassword());
        }
        SysUserInfo user = userService.selectUserByUserName(username);
        if (user != null) {
            //是否通过 域认证
            //boolean flag = ldapAuthentication.authentication(username, password);
            boolean flag = username.equals(user.getSysUsername())&&password.equals(user.getSysPwd());/**/
            if (flag) {
                // 需要验证用户是否在本环境中存在
               //SysUserInfo user = userService.selectUserByUserName(username);
               /*  if (user == null) { // 如果没有此用户, 则添加一个用户
                    user = userService.addNewUser(username);
                } else if (user.getIsDeleted()) {
                    throw new UnknownAccountException("已通过域认证,用户被锁定,请联系管理员。");// 账号锁定
                }
                if (user == null) {
                    throw new UnknownAccountException("已通过域认证,创建用户失败。");
                }*/
                //如果身份认证验证成功,返回一个AuthenticationInfo实现;  
                SimpleAuthenticationInfo info = null;
                try {
                    info = new SimpleAuthenticationInfo(user, password.toCharArray(), user.getSysUsername());
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
                return info;
            } else {
                log.info(username+"用户名或密码错误。");
                throw new UnknownAccountException("用户名或密码错误");
            }
        }else{
             log.info(username+"用户名不存在");
             throw new UnknownAccountException("用户名或密码错误");
        }
    }
 }
  1. 登录controller,LoginController
@Controller
public class LoginController {

    private static Logger log = LoggerFactory.getLogger(LoginController.class);

    /**
     * 访问需要登录的页面时被拦截后,需要跳转到到login页面
     * 结合shiro配置查看
     * @throws IOException
     */
    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String login(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
        log.warn("shiro拦截到需要登录的请求: "+ httpServletRequest.getRequestURI());

        Subject subject = SecurityUtils.getSubject();
        boolean loginStatus = subject.isAuthenticated();
        if (loginStatus) {
            log.warn("已登录状态,返回到主页");
            Cookie coo = new Cookie("loginMessage", null);
            httpServletResponse.addCookie(coo);
            return "redirect:login/index";
        } else {
            log.warn("未登录状态,返回登录页面");
            Cookie cook = new Cookie("loginMessage", null);
            httpServletResponse.addCookie(cook);
            return "/pages/login";
        }
    }

    @RequestMapping(value = "login/index")
    public String toindex(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
        log.warn("登录成功,跳转到主页");
        return "/pages/index";
    }

    /**
     * 登录失败会调用此接口,返回登录界面
     * @throws IOException
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public String loginFail(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException{
        Subject subject = SecurityUtils.getSubject();
        boolean loginStatus = subject.isAuthenticated();
        if (loginStatus) {
            log.warn("已登录状态,返回到主页");
            Cookie coo = new Cookie("loginMessage", null);
            httpServletResponse.addCookie(coo);
            return "redirect:/index";
        } else {
          //登录失败了 提取错误消息
            Exception shiroLoginFailureEx = (Exception) httpServletRequest.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
            String errorMsg = shiroLoginFailureEx.getMessage();
            log.warn("登录失败后返回login页面");
            Cookie coo = new Cookie("loginMessage", URLEncoder.encode(errorMsg,"UTF-8"));
            httpServletResponse.addCookie(coo);
            return "/pages/login";
        }
    }
}

顺序:提交表单后-->CustomFormAuthenticationFilter-->UserRealm-->LoginController(/login)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,088评论 19 139
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 14,660评论 4 61
  • 22年12月更新:个人网站关停,如果仍旧对旧教程有兴趣参考 Github 的markdown内容[https://...
    tangyefei阅读 35,309评论 22 257
  • 一、架构 要学习如何使用Shiro必须先从它的架构谈起,作为一款安全框架Shiro的设计相当精妙。Shiro的应用...
    ITsupuerlady阅读 8,879评论 4 32
  • 下雨的天,路上的行人纷纷躲雨。带雨伞的则很从容撑开伞,在雨中慢步走向目的地。 小布偶眼珠不断地转圈,一直打量没带伞...
    Curiouster阅读 2,372评论 0 0

友情链接更多精彩内容