Shiro框架简单使用


Shiro简单使用

1. Shiro过滤器&标签简介

Shiro过滤器
过滤器简称对应的java类
anonorg.apache.shiro.web.filter.authc.AnonymousFilter
authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter
authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
portorg.apache.shiro.web.filter.authz.PortFilter
restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter
sslorg.apache.shiro.web.filter.authz.SslFilter
userorg.apache.shiro.web.filter.authc.UserFilter
logoutorg.apache.shiro.web.filter.authc.LogoutFilter

Shiro的过滤器的作用,用于用户访问页面(URL)时进行权限控制。

Shiro的JSP标签
标签名称标签条件(满足条件,才显示标签内容)
<shiro:authenticated >登录之后
<shiro:notAuthenticated >不在登录状态时
<shiro:guest >用户在没有RememberMe时
<shiro:user >用户在RememberMe时
<shiro:hasAnyRoles name=“abc,123” >在有abc或者123角色时
<shiro:hasRole name=“abc”>拥有角色abc
<shiro:lacksRole name=“abc”>没有角色abc
<shiro:hasPermission name=“abc”>拥有权限资源abc
<shiro:lacksPermission name=“abc”>没有abc权限资源
<shiro:principal >默认显示用户名称

Shiro的标签的作用,用于对JSP页面的元素(按钮,表单,超链接等)进行权限控制。

2. Shiro登陆认证(一)使用认证过滤器

目标

在项目中使用认证过滤器拦截资源(该拦截的拦截,该放行的放行)

实现

在applicationContext-shiro.xml添加认证过滤器

<!--1.Shiro处理认证授权逻辑的对象-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--安全管理器-->
        <property name="securityManager" ref="securityManager"/>


        <!--设置Shiro的过滤器-->
        <!--
          过滤的路径问题:
            /index.jsp :  准确拦截/index.jsp页面
            /user/*  : 模糊匹配,拦截user根目录下的所有资源(代表一级目录)
            /user/**  : 模糊匹配,拦截user目录下的所有资源(代表任意级目录)

          1. 认证相关的过滤器
             anon: 匿名过滤器,不用认证也可以访问该资源(必须放在authc的前面)
             authc:认证过滤器,必须认证成功才可以访问该资源,如果认证不成功,会自动跳转到login.jsp页面(可以通过loginUrl属性更新登录页面)
        -->
        <property name="filterChainDefinitions">
            <value>
                /css/**=anon
                /img/**=anon
                /make/**=anon
                /plugins/**=anon
                /login.do=anon
                /**=authc
            </value>
        </property>

        <!--修改默认登录页面-->
        <property name="loginUrl" value="/login.jsp"/>

    </bean>

anon:代表不认证也可以访问(匿名访问),通常对静态资源进行放行

authc:代表必须通过认证才可以访问,通常对动态资源(controller,jsp页面)进行拦截,如果用户没有认证,Shiro会自动跳转到默认登录页面login.jsp页面,可以通过以下配置更改:

<property name="loginUrl" value="/admin_login.jsp"/>
注意事项: /login.do因为是登录请求,必须使用anon过滤器放行!!!否则无法登录啦!

3. Shiro登陆认证(二)完成登录认证(*)

目标

实现登陆认证。

  1. 修改LoginController,通过shiro实现登陆认证
  2. 编写AuthRealm,实现登陆认证
  3. 测试

在这里插入图片描述
1.修改LoginController,通过shiro实现登陆认证

/**
     * 登录方法
     *  1)URL: http://localhost:8080/login.do
     *  2)参数:email=eric&password=123
     *  3)返回:/WEB-INF/pages/home/main.jsp
     */
    @RequestMapping("/login")
    public String login(String email,String password){
        //1.判断是否为空
        if(StringUtils.isEmpty(email) || StringUtils.isEmpty(password)){
            request.setAttribute("error","邮箱和密码不能为空");
            //手动转发到login.jsp页面
            return "forward:/login.jsp";
        }

        //使用Shiro的登录认证逻辑

        //1.获取Subject对象
        Subject subject = SecurityUtils.getSubject();

        //2.封装需要认证的信息(用户名和密码)
        UsernamePasswordToken token = new UsernamePasswordToken(email,password);

        //3.调用Subject的login方法进行认证
        //4.判断login方法有无异常,有异常代表认证失败,无异常代表认证成功
        try {
            subject.login(token);

            //无异常代表认证成功

            //5.从Shiro的Subject中获取登录用户对象:  subject.getPrincipal()
            User loginUser = (User)subject.getPrincipal();

            //把登录用户对象存入session域 (注意:这里存入的loginUser对象,不是给Shiro使用的,是给我们自己业务使用的)
            session.setAttribute("loginUser",loginUser);

            //查询当前用户拥有的菜单(模块)
            List<Module> menuList = moduleService.findModuleByUser(loginUser);
            //菜单必须存入session域
            session.setAttribute("menus",menuList);

            //跳转到主页
            return "home/main";
        } catch (UnknownAccountException e) {  //UnknownAccountException: 该异常代表用户名不存在
            request.setAttribute("error","Shrio:用户名不存在");
            return "forward:/login.jsp";
        } catch (IncorrectCredentialsException e) {  //IncorrectCredentialsException: 该异常代表密码错误
            request.setAttribute("error","Shrio:密码输入有误");
            return "forward:/login.jsp";
        } catch (Exception e) {  //
            request.setAttribute("error","Shrio:服务器错误");
            return "forward:/login.jsp";
        }
        /*//2.判断email是否存在
        User loginUser = userService.findByEmail(email);

        //3 email不存在,提示"用户名不存在"
        if(loginUser==null){
            request.setAttribute("error","用户名不存在");
            //手动转发到login.jsp页面
            return "forward:/login.jsp";
        }

        //4 email存在,继续判断password是否正确
        if(loginUser.getPassword().equals(password)){
            //5. password正确,登录成功,保存用户数据到session域中,跳转到主页
            session.setAttribute("loginUser",loginUser);

            //查询当前用户拥有的菜单(模块)
            List<Module> menuList = moduleService.findModuleByUser(loginUser);
            //去除重复元素
            removeDuplicate(menuList);
            //菜单必须存入session域
            session.setAttribute("menus",menuList);


            // 路径: /WEB-INF/pages/home/main.jsp
            return "home/main";
        }else{
            //6.password不正确,提示"密码错误"
            request.setAttribute("error","密码错误");
            //手动转发到login.jsp页面
            return "forward:/login.jsp";
        }*/


    }

2.编写AuthRealm,实现登陆认证

/**
 * 自定义Realm
 */
public class AuthRealm extends AuthorizingRealm{
    @Autowired
    private UserService userService;

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行授权方法...");
        return null;
    }

    //认证 (login方法固定执行doGetAuthenticationInfo方法)
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //System.out.println("执行认证方法...");

        //1.判断用户名是否存在
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        String email = token.getUsername();

        User loginUser = userService.findByEmail(email);
        if(loginUser==null){
            //用户名不存在
            //我们只需要return null即可,Shiro底层判断为null则抛出UnKnowAccountException异常
            return null;
        }

        //2.返回数据库保存密码给Shiro,让Shiro判断密码是否正确
        /**
         * Shiro底层获取SimpleAuthenticationInfo的参数,例如获取password判断和用户输入的密码是否一致
         *    1)如果密码不一致,抛出IncorrectCredentialsException异常
         *    2)如果密码一致, 把principal存入session域
          */    

        //SimpleAuthenticationInfo: 封装认证数据对象
        /**
         * 参数一: principal  登录用户对象,用于Subject.getPrincipal()方法获取
         * 参数二: password   数据库的密码
         * 参数三: realm的别名, 在多个Realm的情况下使用,用于区分用户表的。只有一张用户表不需要别名
         */
        return new SimpleAuthenticationInfo(loginUser,loginUser.getPassword(),"");
    }
}

总结
  1. Shiro登录认证的流程是怎样的?

​ LoginController的login方法调用Subject.login()

​ AuthRealm的认证方法,编写判断用户名和返回数据库密码的逻辑

  1. 请简单描述Realm的认证方法(doGetAuthenticationInfo)代码流程?

​ 1)获取用户输入的信息

​ 2)判断用户名是否存在

​ 3)返回SimpleAuthenticationInfo,保存数据库密码和用户登录信息等

4. Shiro登陆认证(三)凭证匹配器-普通加密
需求

目前对用户输入的密码,我们直接输入原文密码。这样不安全,现在我们需要对用户输入的密码md5加密 及 md5加盐加密。

加密方式:

1)普通加密(md5,sha1,sha2等)

2)加盐加密(推荐使用

步骤

1)对数据库密码进行md5加密 (略)

2)在applicationContext-shiro.xml,添加加密认证配置

3)测试

1.略
2.在applicationContext-shiro.xml,添加加密认证配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1.Shiro处理认证授权逻辑的对象-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--安全管理器-->
        <property name="securityManager" ref="securityManager"/>


        <!--设置Shiro的过滤器-->

        <!--
          过滤的路径问题:
            /index.jsp :  准确拦截/index.jsp页面
            /user/*  : 模糊匹配,拦截user根目录下的所有资源(代表一级目录)
            /user/**  : 模糊匹配,拦截user目录下的所有资源(代表任意级目录)

          1. 认证相关的过滤器
             anon: 匿名过滤器,不用认证也可以访问该资源(必须放在authc的前面)
             authc:认证过滤器,必须认证成功才可以访问该资源,如果认证不成功,会自动跳转到login.jsp页面(可以通过loginUrl属性更新登录页面)
        -->
        <property name="filterChainDefinitions">
            <value>
                /css/**=anon
                /img/**=anon
                /make/**=anon
                /plugins/**=anon
                /login.do=anon
                /**=authc
            </value>
        </property>

        <!--修改默认登录页面-->
        <property name="loginUrl" value="/login.jsp"/>

    </bean>

    <!--2.配置安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--配置Realm-->
        <property name="realm" ref="myRealm"/>
    </bean>

    <!--3.配置Realm-->
    <bean id="myRealm" class="cn.itcast.web.shiro.AuthRealm">
        <!--配置凭证匹配器-->
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>

    <!--创建Shiro自带的凭证匹配器-->
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <!--指定需要加密的算法名称-->
        <property name="hashAlgorithmName" value="md5"/>
    </bean>
</beans>
总结

请简单说出Shiro加密判断的执行流程

​ Subject.login(token) (未加密数据) -> AuthRealm的认证方法->返回数据库密码->先把token密码使用算法加密,再和数据库的密码匹配

5. Shiro登陆认证(四)凭证匹配器-加盐加密

需求

如果只是单纯使用md5或sha1进行加密,容易被人利用"彩虹表"撞库来破解密码,导致密码不安全!这时可以进行加盐加密来解决。

什么是加盐加密

加密流程: 使用md5算法加密1次 + 盐(变量,每个用户不同的)= 2次使用md5加密

步骤

1)编写代码对密码加盐加密

2)编写自定义凭证匹配器

3)在applicationContext-shiro.xml,添加自定义凭证匹配器

4)测试

加盐加密实现

1)编写代码对密码加盐加密

/**
 * 加盐加密的工具
 */
public class Demo {

    public static void main(String[] args) {
        //1.原密码
        String password = "123";

        //2.盐
        String salt = "lw@export.com";

        //3.加盐加密
        /**
         * 参数一:原密码
         * 参数二:盐
         * 参数三(可选):加密次数,默认1次
         */
        Md5Hash md5Hash = new Md5Hash(password,salt);

        System.out.println(md5Hash.toString());
    }

}

2)编写自定义凭证匹配器

/**
 * 自定义凭证匹配器
 *  加盐加密的匹配
 */
public class CustomCredentialsMatcher extends SimpleCredentialsMatcher{

    /**
     * 完成密码判断的逻辑
     * @param token  包含了用户输入的密码
     * @param info   包含了数据库的密码
     * @return 密码判断结果
     *       true: 代表数据库的密码和用户输入的密码一致的
     *       false:代表数据库的密码和用户输入的密码不一致的
     */
    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {

        //1.获取用户输入的密码
        UsernamePasswordToken userToken = (UsernamePasswordToken)token;
        String userPassword = new String(userToken.getPassword()); // ['1','2','3']

        //获取用户邮箱
        String email = userToken.getUsername();

        //2.对用户输入的密码进行加盐加密
        Md5Hash md5Hash = new Md5Hash(userPassword,email);
        String encodePwd = md5Hash.toString();

        //3.获取数据库的密码
        String dbPwd = (String)info.getCredentials();

        //4.判断数据库的密码和第二步产生的密码是否一致,一致返回true,不一致返回false
        return dbPwd.equals(encodePwd);
    }
}

3)在applicationContext-shiro.xml,添加自定义凭证匹配器

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--1.Shiro处理认证授权逻辑的对象-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--安全管理器-->
        <property name="securityManager" ref="securityManager"/>


        <!--设置Shiro的过滤器-->

        <!--  过滤的路径问题:
            /index.jsp :  准确拦截/index.jsp页面
            /user/*  : 模糊匹配,拦截user根目录下的所有资源(代表一级目录)
            /user/**  : 模糊匹配,拦截user目录下的所有资源(代表任意级目录

          1. 认证相关的过滤器
             anon: 匿名过滤器,不用认证也可以访问该资源(必须放在authc的前面)
             authc:认证过滤器,必须认证成功才可以访问该资源,如果认证不成功,会自动跳转到login.jsp页面(可以通过loginUrl属性更新登录页面-->
         
        <property name="filterChainDefinitions">
            <value>
                /css/**=anon
                /img/**=anon
                /make/**=anon
                /plugins/**=anon
                /login.do=anon
                /**=authc
            </value>
        </property>

        <!--修改默认登录页面-->
        <property name="loginUrl" value="/login.jsp"/>

    </bean>

    <!--2.配置安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--配置Realm-->
        <property name="realm" ref="myRealm"/>
    </bean>

    <!--3.配置Realm-->
    <bean id="myRealm" class="cn.itcast.web.shiro.AuthRealm">
        <!--配置凭证匹配器-->
        <property name="credentialsMatcher" ref="credentialsMatcher"/>
    </bean>

    <!--创建Shiro自带的凭证匹配器-->
    <!--<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        &lt;!&ndash;指定需要加密的算法名称&ndash;&gt;
        <property name="hashAlgorithmName" value="sha1"/>
    </bean>-->
    
    <!--创建自定义凭证匹配器-->
    <bean id="credentialsMatcher" class="cn.itcast.web.shiro.CustomCredentialsMatcher"/>
</beans>
总结

1)数据库密码更新为加盐加密的密码

2)自定义凭证匹配器类,编写判断密码逻辑

3)applicationContextx-shiro.xml添加自定义凭证匹配器

6. Shiro登陆认证(五)用户添加时密码加密

注意:添加用户时候,需要对用户输入的密码加密、加盐,这样就可以使用添加的用户登陆了。
UserServiceImpl添加加密逻辑

 @Override
    public void save(User user) {
        //生成主键
        user.setId(UUID.randomUUID().toString());
        
        //对密码加盐加密
        Md5Hash md5Hash = new Md5Hash(user.getPassword(),user.getEmail());
        user.setPassword(md5Hash.toString());
        
        userDao.save(user);
    }

7. Shiro登录注销

需求

如果我们项目引入了Shiro,那么登录注销也要按照Shiro的方式来注销。

实现

Shiro的登录注销代码实现:

/**
     * 登录注销
     *  1)http://localhost:8080/logout.do
     *  2)参数:无
     *  3)返回:/login.jsp
     */
    @RequestMapping("/logout")
    public String logout(){
        //删除session的登录数
        session.removeAttribute("loginUser");
        session.removeAttribute("menus");

        //Shiro登录注销(底层:删除之前Shiro存入的session的key)
        Subject subject = SecurityUtils.getSubject();
        subject.logout();

        return "redirect:/login.jsp";
    }

8. Shiro授权(一)介绍

目标
  1. 什么是授权,如何实现授权?
  2. shiro授权校验有几种方式
什么是授权,如何实现授权?
  1. 授权,也叫做授权访问资源 或者说 校验是否可以访问资源

  2. 登陆认证后,系统校验用户是否有权限访问资源,就叫授权。

  3. 如何实现授权?分为以下两个步骤

    1. 登陆认证成功后,获取用户的权限 (获取权限
    2. 访问资源时候,进行授权校验:用访问资源需要的权限去用户权限列表查找,如果存在,则有权限访问资源。(权限拦截)
Shiro授权校验有几种方式

shiro提供了四种方式实现权限校验:

1) 硬编码方式(拦截方法)(非Web应用,Web应用)

Subject subject = SecurityUtils.getSubject();
subject.checkPermission("部门管理");

2) 过滤器(XML)配置方式(拦截url)(Web应用)

/system/user/list.do = perms["用户管理"]

3) 注解方式(拦截方法)(Web应用)

@RequiresPermissions(“”)

4) shiro提供的JSP标签((拦截页面元素:按钮,表格等))(Web应用)

<shiro:hasPermission name="用户管理">
    <a href="#">用户管理</a>
</shiro:hasPermission>
总结

什么是授权?授权有哪两步?

授权:用户认证成功后,进一步对访问资源进行权限控制

授权的两步:

1)获取用户权限 (买票)

2)校验用户权限(验票)

9. Shiro授权(二)完成用户授权方法

目标

实现自定义realm的doGetAuthorizationInfo()方法,返回用户已经具有的权限。

实现
 //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //System.out.println("执行授权方法...");

        //从Subject获取当前登录用户
        Subject subject = SecurityUtils.getSubject();
        User loginUser = (User)subject.getPrincipal();
		//1、获取当前登录用户拥有的权限
        List<Module> moduleList = moduleService.findModuleByUser(loginUser);

        //2、把当前登录用户的权限告诉Shiro框架
        //2.1 创建授权对象
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //2.2 把模块存入授权对象
        if(moduleList!=null && moduleList.size()>0){
            for(Module module:moduleList){
                if(!StringUtils.isEmpty(module.getName())) {
                    //往授权对象存入授权信息(授权信息存入什么字符串? 字符串必须唯一的)
                    info.addStringPermission(module.getName());
                }
            }

        }
        return info;
    }
总结

Shiro的授权第一步:获取用户权限的逻辑

1)查询当前用户拥有的权限(模块)

2)把所有权限存入AuthorizationInfo对象(注意:存入的只是权限标记即可,标记必须唯一)

3)返回AuthorizationInfo对象对象

10. Shiro授权(三)硬编码方式实现授权验证(了解)

问题

我们以老王用户登录,可以看到老王拥有”用户管理“的菜单,他可以访问”用户“。但是老王其实没有”企业管理“的菜单,但是通过浏览器URL依然可以访问到”企业管理"内容。

思考

这种情况如何解决? 只有一种办法: 后台做授权验证,当用户有权限才可以访问,否则不能访问.

硬编码方式授权验证

只需要在控制器中添加如下2行代码即可:
在这里插入图片描述
在这里插入图片描述
测试结果 :报错,没有权限访问。

总结

Shiro硬编码方式的核心代码是什么?

在需要验证的方法的前面加上Subject.checkPermissions(“权限标记”) 标记取决于存入时的内容

11. Shiro授权(四)XML配置方式实现授权验证(推荐)

需求
  1. 通过XML配饰方式实现需求: 有用户管理的权限,才可以访问; 否则拒绝访问。

Shiro授权过滤器实现授权验证(XML方式)
在applicationContext-shiro中
在这里插入图片描述

<property name="filterChainDefinitions">
            <value>
                /css/**=anon
                /img/**=anon
                /make/**=anon
                /plugins/**=anon
                /login.do=anon
                <!--左边为访问的资源  右边为需要的权限-->
                /system/user/list.do=perms["用户管理"]
                /company/list.do=perms["企业管理"]
                /**=authc
            </value>
        </property>
总结

Shiro的哪个过滤器可以实现授权验证?

perms 格式:/system/user/list.do = perms[“用户管理”]

12. Shiro授权(五)注解方式实现授权验证(了解)

需求
  1. 通过注解配置方式实现需求: 有用户管理的权限,才可以访问; 否则拒绝访问。
注解方式实现授权验证

现在通过注解实现权限控制,分为2步骤:

  1. 在applicationContext-shiro.xml中开启shiro注解支持
  2. 开启Aop自动代理(已经完成)
  3. 在controller中使用@RequiresPermissions(“”)注解

实现:

  1. 在applicationContext-shiro.xml中开启shiro注解支持

    添加如下配置:

<!--====开启Shiro的注解====-->
    <!--
        Shiro注解借助了Spring的AOP来实现授权校验
    -->
    <bean id="lifecycleBeanPostProcessor"
          class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

2.开启Aop自动代理(已经完成)

<!--6. 开启Aop自动代理-->
<aop:aspectj-autoproxy/>

3.在controller中使用@RequiresPermissions(“”)注解
在这里插入图片描述

13. Shiro授权(六)Shiro标签实现授权验证

编写test-auth.jsp

在shiro-test.jsp中,通过shiro标签实现权限控制

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试Shiro的jsp标签</title>
</head>
<body>

<shiro:hasPermission name="企业管理">
<a href="">企业管理</a>
</shiro:hasPermission>

<hr/>

<shiro:hasPermission name="用户管理">
<a href="">用户管理</a>
</shiro:hasPermission>

</body>
</html>

测试

以老王(企业管理员)登录就只能看到用户管理,以张三(Saas管理员)登录就只能看到企业管理

14. Shiro流程总结

关键代码
认证: subject.login(token);

授权: subject.checkPermission("");

认证和授权流程
在这里插入图片描述

15. 总结

1)Shiro认证(重点)

​ 1.1 配置认证过滤器

​ 1.2 编写认证代码逻辑

​ 1.3 密码加密

2)Shiro授权

​ 2.1 获取用户的权限

​ 2.2 掌握四种权限校验方法(XML配置,JSP标签)

上课课件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值