shiro-入门,快速与springmvc整合,全注解,最简单让项目运行起来

本文详细介绍如何在SpringBoot项目中整合Shiro框架,包括添加Shiro过滤器、配置安全管理器及自定义过滤器等内容,并提供了具体代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

笔记:springboot、springmvc全注解入门整合shiro

功能介绍

2015-11-19注 :最近看了架构探险中对shiro的讲解,下面这篇文章,在使用的方式上是正确的,有一些配置和使用至于是为什么,则需要你们自己去看架构探险章节的说明了,时隔一年来看这篇文章还是觉得很有价值,在当时也只会用使用能满足自己的要求。
本文的认证方法处理有误,正确的应该是:login提交用户表单提交的用户名和密码,doGetAuthenticationInfo认证方法里面放置正确的用户名和密码,让框架去匹配是否通过认证,以抛出异常的方式进行通知调用处没有通过认证,session中不会存放该用户的信息。

该示例:大概功能如下:
由于角色可以自由创建,拥有权限分配的功能的帐号,可以把自身的权限分配给创建的角色。所以本人只想到了,使用请求地址来控制每一个权限。
整合开始:

1.添加shiroFilter过滤器

在能替代web.xml的配置类中添加过滤器

/**
 * 用注解方式,类里面配置,完全取代 web.xml中的配置
 * @author admin
 */
@Order(1) //第一启动顺序
@HandlesTypes(WebApplicationInitializer.class) //可能是标识这个是一个web上下文初始化的类(在springboot中该类需要在WebMvcConfigurerAdapter(可自定义继承该类)中加载调用)
public class WebXml implements ServletContextInitializer {
    Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void onStartup(ServletContext servletContext) throws ServletException {
        logger.info("加载shiro过滤器,与spring集成");
        /**
         * 找名字为shiroFilter(filter-name)的bean 并把所有Filter的操作委托给它。然后将ShiroFilter
         * 配置到spring容器即可
         */
        Dynamic shiroFilter = servletContext.addFilter("shiroFilter", DelegatingFilterProxy.class);
        shiroFilter.setInitParameter("argetFilterLifecycl", "true");
        shiroFilter.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
        shiroFilter.setAsyncSupported(true);
    }
}

2.配置WebMvcConfigurer

下面的类中有点多,但是指需要添加与shiro先关的几个bean就成了。
之前的是在项目中配置好的

public class WebConfig extends WebMvcConfigurerAdapter {
    Logger logger = LoggerFactory.getLogger(getClass());

    @Bean
    public ServletContextInitializer getMyFilter() {
        logger.info("注册web.xml中对应的配置都可以在这里类里面编写-----------");
        return new WebXml();
    }

    /**
     * 添加全局异常处理器 1:这里好像是不用屏蔽 springmvc自动注册的全局异常处理器 2:编写自定义处理器 实现
     * HandlerExceptionResolver 3:在这里加载
     * */
    @Bean
    public HandlerExceptionResolver getHandlerExceptionResolver() {
        logger.info("加载自定义异常处理器");
        return new ExceptionResolverCustom();
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        logger.info("自定义拦截器在这里增加");
        registry.addInterceptor(new LoginInterceptor());
        super.addInterceptors(registry);
    }
    /*
     * @Bean public InternalResourceViewResolver
     * getInternalResourceViewResolver() { logger.info("加载视图解析器,配置springmvc的视图解析器,一般用于jsp开发?");
     * InternalResourceViewResolver resolver = new
     * InternalResourceViewResolver(); resolver.setSuffix(".html"); return
     * resolver; }
     */
    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer() {
        logger.info("加载错误页面相关处理,spring boot中可以对根据对应的状态码,比如404,等添加指定的页面");
        return new MyCustomizer();
    }
//会话ID生成器 
    @Bean
    public SessionIdGenerator sessionIdGenerator() {
        JavaUuidSessionIdGenerator jusg = new JavaUuidSessionIdGenerator();
        return jusg;
    }

//    <!-- 会话DAO,用来方便的获得 当前用户的session,也就是说,在任何一个普通类里面都可以使用Subject subject = SecurityUtils.getSubject();  Session session = subject.getSession();   这个代码来获取当前用户的session
 -->
    @Bean
    public EnterpriseCacheSessionDAO sessionDao() {
        EnterpriseCacheSessionDAO ecd = new EnterpriseCacheSessionDAO();
        ecd.setSessionIdGenerator(this.sessionIdGenerator());
        ecd.setCacheManager(this.shiroCacheManager());
        return ecd;
    }
    private EhCacheManager ec  = null;
    //缓存,这个缓存,没有集成成功。
    @Bean
    public EhCacheManager shiroCacheManager() {
        if(ec == null) {
            ec = new EhCacheManager();
            ec.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
        }
        return ec;
    }

    //会话管理器
    @Bean
    public SessionManager sessionManager() {
        DefaultWebSessionManager dwsm = new DefaultWebSessionManager();
        dwsm.setGlobalSessionTimeout(1800000); // 超时
        dwsm.setDeleteInvalidSessions(true);
//      dwsm.setSessionValidationSchedulerEnabled(true);
//      dwsm.setSessionValidationScheduler(this.);
//      dwsm.setCacheManager(this.shiroCacheManager());
        dwsm.setSessionDAO(this.sessionDao());
        return dwsm;
    }

    //安全管理器
    @Bean
    public SecurityManager securityManager() {
        UserRealm singleRealm = new UserRealm();
        singleRealm.setAuthorizationCachingEnabled(true);
        DefaultWebSecurityManager dwsm = new DefaultWebSecurityManager(singleRealm); //加载自定义realm域
        dwsm.setSessionManager(this.sessionManager());
        return dwsm;
    }

    //<!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
    @Bean
    public MethodInvokingFactoryBean methodInvokingFactoryBean() {
        MethodInvokingFactoryBean mifb = new MethodInvokingFactoryBean();
        mifb.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        mifb.setArguments(new Object[] {this.securityManager()});
        return mifb;
    }
    //web过滤器
    @Bean(name="shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean () {
        ShiroFilterFactoryBean sffb = new ShiroFilterFactoryBean();
        sffb.setSecurityManager(this.securityManager());
        sffb.setLoginUrl("/login.html");//设置登录页面
        sffb.setUnauthorizedUrl("/unAuthc.html"); //未授权跳转的页面
        Map<String, Filter> filters = new HashMap<>();
        filters.put("sysUser", new SysUserFilter()); //自定义过滤器,我是用它来拦截自定义的权限监测调用
        sffb.setFilters(filters);
        //设置对应的url对应的过滤链
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        filterChainDefinitionMap.put("/login.html", "anon");
        filterChainDefinitionMap.put("/system/login/login", "anon");
        filterChainDefinitionMap.put("/system/login/logOut", "anon");
        filterChainDefinitionMap.put("/errorCustom.html", "anon");
        filterChainDefinitionMap.put("/unAuthc.html", "anon");
        filterChainDefinitionMap.put("/views/app.js", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        filterChainDefinitionMap.put("/imag/**", "anon");
        filterChainDefinitionMap.put("/wxMessagePic/*", "anon");
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/**", "sysUser");
//可以看到对于一些公共的地址我进行了放行,对于/**使用了自定义的过滤器进行处理,意思就是说:除了以上放行的地址,其他的地址都会被调用自定义的过滤器方法中      
        sffb.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return sffb;
    }

    //shiro 生命周期处理器
    @Bean
    public LifecycleBeanPostProcessor LifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
}

3.编写自定义UserRealm

获取spring容器思路:12

import org.apache.shiro.realm.AuthorizingRealm;
public class UserRealm extends AuthorizingRealm {

    /**
     * 获取授权信息,用户所拥有的角色和权限
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
         //这里能得到这个参数,就是在下面的doGetAuthenticationInfo返回的用户验证信息。(这个是单realm的实现,直接调用这个方法,就能获得自定义的对象了)
        ShiroPrincipal shiroPrincipal = (ShiroPrincipal) principals.getPrimaryPrincipal();

        Set<String> psIds = null;
        if (shiroPrincipal.isAuthorized()) {
            psIds = shiroPrincipal.getAuthorities();
        } else {
            ISecurityService iss = Config.webApplicationContext.getBean(ISecurityService.class);
            try {
                psIds = iss.findByUsername(shiroPrincipal.getUsername());
                shiroPrincipal.setAuthorized(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        SimpleAuthorizationInfo si = new SimpleAuthorizationInfo();
        si.setStringPermissions(psIds); // setRoles 暂时只实现权限判断
        return si;
    }

    /**
     * 获取身份验证相关信
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //这个token,是在登录方法里面,调用login方法,把用户名和密码传递过来的
        //Subject subject = SecurityUtils.getSubject();
        //subject.login(new UsernamePasswordToken(systemVo.getUsername(), systemVo.getPassword()));
        String username = (String) token.getPrincipal(); // 得到用户名
        String password = new String((char[]) token.getCredentials()); // 得到密码

        // 如果身份认证验证成功,返回一个AuthenticationInfo实现
        // 这里处理的流程:暂时是,先通过了系统的自定义流程之后,再调用shiro的login功能,跳转到这里,所以
        // 就不用去获取用户等数据了。直接返回对象:简单的对象实体 new SimpleAuthenticationInfo(username,
        // password, getName());
        // 第一个参数,可以在授权方法中获取到
        ShiroPrincipal principal = new ShiroPrincipal(username);
        return new SimpleAuthenticationInfo(principal, password, this.getName());
    }
}

4.编写自定义过滤器

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.PathMatchingFilter;
public class SysUserFilter extends PathMatchingFilter {


    @Override
    protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        HttpServletRequest req = (HttpServletRequest) request;   
        HttpServletResponse res = (HttpServletResponse) response;   
        String url = req.getRequestURI();// 用户请求的地址

        Subject subject = SecurityUtils.getSubject();
        String username = (String)subject.getPrincipal();
        if("superadmin".equals(username)) { //系统超级管理员放行
            return true;
        }

        Integer rulKey = this.matchingURL(url); 
        if(subject.isPermitted(rulKey+"")) { //如果有权限。则放行
            return true;
        }else {
            String method = req.getMethod();
            String header = req.getHeader("Accept");
            if(header.startsWith("application/json")) {

             JSONObject responseJSONObject = new JSONObject();  
             responseJSONObject.put("0", "你没有该权限");

             res.setContentType("application/json; charset=utf-8");  
             res.setCharacterEncoding("UTF-8");  
             PrintWriter out = null;  

             out = res.getWriter();  
             out.append(responseJSONObject.toString());  
            }
        }
        res.sendRedirect("/unAuthc.html");
        return false;
    }
    /**
     * 
     * @Title: matchingURL 
     * @Description:根据当前请求地址获取标准的公共权限地址,只要是正常的访问。就应该能和公共权限中 一一对应查找到 
     * @param currentUrl 
     * @return Integer 获取到后,返回该公共地址的权限id,返回null,则表示请求的资源在公共权限库中没有找到(待定义处理)
     * @version V0.1  
     * @createDate 2015年3月18日 下午4:00:40 
     * @updateDate 2015年3月18日 下午4:00:40
     */
    private Integer matchingURL(String currentUrl) {
        Map<Integer, String> authUrls = StaticCache.authUrls;
        for (Map.Entry<Integer, String> url : authUrls.entrySet()) {
            if(currentUrl.contains(url.getValue())) { //匹配上
                return url.getKey();
            }
        }
        return null;
    }
}

5.总结

用一张图来表示,shiro大概的入门调用流程,这个是自己花了一天时间理解的。也不知道对不对。反正就先这样用着先。
shiro调用简单流程

总结步骤:
1.添加shiro过滤器入口(一定要保证白过滤器在其他过滤器之前,至少在配置清单中在最前面)
2:配置安全管理器,设置自定义realm
3:配置web过滤器(也就是定义shiroFilter,定义的入口会搜寻该对象),设置权限,设置自定义过滤器拦截权限处理


  1. 我是专门做了一个监听器,在监听器中利用servletcontext来获取到spring的容易,然后存入一个工具类中的静态常量中。也就是说这样操作之后,在一个普通的java类中也可以获得spring容器
  2. 用一个工具类实现spring的接口,然后用spring容器初始化该工具类,就能获取容器了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值