• 使用Shiro实现权限验证


    《使用Shiro实现权限验证》

    1. Shiro入门

    ApacheShiro是一个功能强大且易于使用的Java安全框架,提供了认证,授权,加密,和会话管理。

    Shiro有三大核心组件:

    Subject:即当前用户,在权限管理的应用程序里往往需要知道谁能够操作什么,谁拥有操作该程序的权利,shiro中则需要通过Subject来提供基础的当前用户信息,Subject 不仅仅代表某个用户,与当前应用交互的任何东西都是Subject,如网络爬虫等。所有的Subject都要绑定到SecurityManager上,与Subject的交互实际上是被转换为与SecurityManager的交互。

    SecurityManager:即所有Subject的管理者,这是Shiro框架的核心组件,可以把他看做是一个Shiro框架的全局管理组件,用于调度各种Shiro框架的服务。作用类似于SpringMVC中的DispatcherServlet,用于拦截所有请求并进行处理。

    Realm:Realm是用户的信息认证器和用户的权限人证器,我们需要自己来实现Realm来自定义的管理我们自己系统内部的权限规则。SecurityManager要验证用户,需要从Realm中获取用户。可以把Realm看做是数据源。

    1.1 搭建测试工程

    新建一个普通的java工程,简单了解一下Shiro的API。

    完整的工程目录如下:

    1.2 导入jar包

    在pom.xml中加入jar包的配置:


    org.apache.shiro
    shiro-core
    1.2.3


    org.slf4j
    slf4j-log4j12
    1.6.1


    org.slf4j
    slf4j-api
    1.6.1

    使用log4j查看日志,需要在resources目录下创建log4j.properties配置文件:

    log4j.rootLogger=INFO, stdout,

    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

    1.3 编写shiro配置文件

    在resources目录下编写shiro配置文件,shiro.ini:

    #用户名=密码,角色1,角色2…,角色n
    [users]
    root = secret, admin
    guest = guest, guest
    test = 123456, role1, role2

    -----------------------------------------------------------------------------

    Roles with assigned permissions

    roleName = perm1, perm2, …, permN

    角色名=权限1,权限2…权限n

    -----------------------------------------------------------------------------

    [roles]
    admin = *
    guest = guest
    role1=perm1,perm2
    role2=perm3

    配置文件中包含两个部分用户[users]和角色[roles]

    用户配置的格式是:

    用户名=密码,角色1,角色2,…角色n

    如:

    test=123456,role1,role2

    用户名是test,密码是123456,拥有两个角色role1和role2

    一个用户可以具有多个角色。注意逗号是英文的。

    角色配置的格式是:

    角色名=权限1,权限2…权限n

    如:

    role1=perm1,perm2

    角色名是role1,拥有perm1和perm2两个权限。

    1.4 测试代码

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    public class ShiroTest {
    private static final transient Logger log =
    LoggerFactory.getLogger(ShiroTest.class);

    public static void main(String[] args) {
    //1. 这里的SecurityManager是org.apache.shiro.mgt.SecurityManager
    // 而不是java.lang.SecurityManager
    // 加载配置文件
    Factory factory =
    new IniSecurityManagerFactory(“classpath:shiro.ini”);
    //2.解析配置文件,并且返回一些SecurityManger实例
    SecurityManager securityManager = factory.getInstance();
    //3.将SecurityManager绑定给SecurityUtils
    SecurityUtils.setSecurityManager(securityManager);
    // 安全操作,Subject是当前登录的用户
    Subject currentUser = SecurityUtils.getSubject();

    // 测试在应用的当前会话中设置属性
    Session session = currentUser.getSession();
    //放进去一个key和一个value
    session.setAttribute(“someKey”, “aValue”);
    //根据key拿到value
    String value = (String) session.getAttribute(“someKey”);
    if (“aValue”.equals(value)) {//比较拿到的值和原来的值是否一致
    log.info(“检索到正确的值[” + value + “]”);
    }
    //尝试进行登录用户,如果登录失败了,我们进行一些处理
    if (!currentUser.isAuthenticated()) {//如果用户没有登录过
    //new UsernamePasswordToken(用户名,密码)
    UsernamePasswordToken token =
    new UsernamePasswordToken(“test”, “123456”);
    token.setRememberMe(true);//是否记住用户
    try {
    currentUser.login(token);
    //当我们获登录用户之后
    log.info(“用户 [” + currentUser.getPrincipal() + “] 登陆成功”);
    // 查看用户是否有指定的角色
    if (currentUser.hasRole(“admin”)) {
    log.info(“您有admin角色”);
    } else {
    log.info(“您没有admin角色”);
    }
    if (currentUser.hasRole(“role1”)) {
    log.info(“您有role1角色”);
    } else {
    log.info(“您没有role1角色”);
    }

    // 查看用户是否有某个权限
    if (currentUser.isPermitted(“perm1”)) {
    log.info(“您有perm1权限”);
    } else {
    log.info(“您没有perm1权限”);
    }
    if (currentUser.isPermitted(“guest”)) {
    log.info(“您有guest权限”);
    } else {
    log.info(“您没有guest权限”);
    }
    //登出
    currentUser.logout();
    } catch (UnknownAccountException uae) {
    log.info(token.getPrincipal() + “账户不存在”);
    } catch (IncorrectCredentialsException ice) {
    log.info(token.getPrincipal() + “密码不正确”);
    } catch (LockedAccountException lae) {
    log.info(token.getPrincipal() + "用户被锁定了 ");
    } catch (AuthenticationException ae) {
    //无法判断是什么错了
    log.info(ae.getMessage());
    }
    }
    }
    }

    运行结果:

    注:rememberMe只能记住你登录过,但不会记住你是谁以及你的权限信息。

    2. Shiro+MySQL动态权限验证

    2.1 数据库设计

    在实际开发中,用户名密码、角色、权限需要存在数据库中动态管理。一个简单的Shiro+MySQL的项目需要三张表:

    用户表SHIRO_USER:

    测试数据:

    用户角色表SHIRO_USER_ROLE:

    测试数据:

    角色权限表SHIRO_ROLE_PERMISSION:

    测试数据:

    2.2 工程搭建

    工程的搭建与第1.1节中介绍的一样。需要在1.2节的基础上加上数据源的jar包和mysql的驱动包,数据源我们选用spring-jdbc:


    mysql
    mysql-connector-java
    5.1.32


    org.springframework
    spring-jdbc
    4.3.11.RELEASE

    2.3 配置文件

    配置文件与1.3中的不同,在使用jdbc数据源的时候,不需要指定user和roles,而是在配置文件中指定数据库连接信息和要执行的sql语句。

    在resources文件夹下创建配置文件shiro-mysql.ini:

    [main]
    dataSource=org.springframework.jdbc.datasource.DriverManagerDataSource
    dataSource.driverClassName=com.mysql.jdbc.Driver
    dataSource.url=jdbc:mysql://127.0.0.1:3306/数据库名
    dataSource.username=用户名
    #如果数据库没有密码,就不要写这行
    dataSource.password=你的密码
    jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
    #是否检查权限
    jdbcRealm.permissionsLookupEnabled = true
    jdbcRealm.dataSource=KaTeX parse error: Expected 'EOF', got '#' at position 12: dataSource #̲重写sql语句 #根据用户名查…jdbcRealm

    注意sql语句,每次只查询一个shiro要求查询的字段,如果写select *就会报错了。

    ini配置文件要求必须是key=value的形式,如果有些人没有设置数据库的密码,就不要写对应的配置。只写”dataSource.password=”等号右面没有值会报错。

    2.4 测试代码

    测试代码与1.4中类似,只是读取的配置文件不同:

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.config.IniSecurityManagerFactory;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.util.Factory;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    public class ShiroMySqlTest {
    private static final transient Logger log =
    LoggerFactory.getLogger(ShiroMySqlTest.class);
    public static void main(String[] args) {
    Factory factory =
    new IniSecurityManagerFactory(“classpath:shiro-mysql.ini”);
    SecurityManager securityManager = factory.getInstance();
    SecurityUtils.setSecurityManager(securityManager);
    Subject currentUser = SecurityUtils.getSubject();
    UsernamePasswordToken token =
    new UsernamePasswordToken(“test@shiro.com”, “123456”);
    token.setRememberMe(true);//是否记住用户
    try {
    currentUser.login(token);
    //当我们获登录用户之后
    log.info(“用户 [” + currentUser.getPrincipal() + “] 登陆成功”);
    //查看用户是否有角色
    if (currentUser.hasRole(“admin”)) {
    log.info(“您有admin角色”);
    } else {
    log.info(“您没有admin角色”);
    }
    if (currentUser.hasRole(“test”)) {
    log.info(“您有test角色”);
    } else {
    log.info(“您没有test角色”);
    }
    // 查看用户是否有某个权限
    if (currentUser.isPermitted(“perm1”)) {
    log.info(“您有perm1权限”);
    } else {
    log.info(“您没有perm1权限”);
    }
    if (currentUser.isPermitted(“guest”)) {
    log.info(“您有guest权限”);
    } else {
    log.info(“您没有guest权限”);
    }
    //登出
    currentUser.logout();
    } catch (UnknownAccountException uae) {
    log.info(token.getPrincipal() + “账户不存在”);
    } catch (IncorrectCredentialsException ice) {
    log.info(token.getPrincipal() + “密码不正确”);
    } catch (LockedAccountException lae) {
    log.info(token.getPrincipal() + "用户被锁定了 ");
    } catch (AuthenticationException ae) {
    //无法判断是什么错了
    log.info(ae.getMessage());
    }
    }
    }

    运行结果:

    3. Web中使用Shiro

    本小节使用SpringJDBC、SpringMVC、Shiro实现一个简单的权限验证。实际开发中数据层可以使用Hibernate、Mybatis等数据层框架。本节的示例只是介绍Web工程中使用Shiro验证的基本流程,完整的权限系统会比这个示例复杂的多。为了简化代码,这个示例中没有写接口。

    这个测试项目用第2小节中的数据进行测试。

    工程中定义几个地址:

    /gologin.html 不需要权限验证就可以访问

    /login.html 不需要验证就可以访问

    /doadd.html 要有perm1和perm2权限才可以访问,访问成功后页面显示add

    /doget.html 要有admin权限才可以访问,访问成功后页面显示get

    /doupdate.html 要有perm1权限才能访问,访问成功后页面显示update

    /dodel.html 要有perm2权限才可以访问,访问成功后页面显示del

    第2小节的测试数据中,test@shiro.com这个用户由perm1和perm2两个权限,没有admin权限,所以/doget.html访问不到,会显示error。

    3.1 创建web工程

    完整的工程目录如下:

    3.2 导入jar包


    org.apache.shiro
    shiro-core
    1.2.3


    org.slf4j
    slf4j-log4j12
    1.6.1


    org.slf4j
    slf4j-api
    1.6.1


    mysql
    mysql-connector-java
    5.1.32


    org.springframework
    spring-web
    4.3.11.RELEASE


    org.springframework
    spring-webmvc
    4.3.11.RELEASE


    org.springframework
    spring-jdbc
    4.3.11.RELEASE


    org.apache.shiro
    shiro-spring
    1.4.0

    3.3 DAO层

    package dao;

    import org.springframework.jdbc.core.JdbcTemplate;

    import java.util.List;

    public class ShiroDAO {
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    this.jdbcTemplate = jdbcTemplate;
    }

    /**
    * 根据用户名查询密码
    */
    public String getPasswordByUserName(String username) {
    String sql = "select PASSWORD from SHIRO_USER where USER_NAME = ";
    String password = jdbcTemplate.queryForObject(sql, String.class, username);
    return password;
    }

    /**
    * 查询当前用户对应的权限
    */
    public List getPermissionByUserName(String username) {
    String sql = "select P.PERM_NAME from SHIRO_ROLE_PERMISSION P inner join SHIRO_USER_ROLE R on P.ROLE_NAME=R.ROLE_NAME where R.USER_NAME = ";
    List perms = jdbcTemplate.queryForList(sql, String.class, username);
    return perms;
    }
    }

    3.4 Service层

    package service;

    import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
    import dao.ShiroDAO;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.subject.Subject;

    import java.util.List;

    public class ShiroService {
    private ShiroDAO shiroDAO;

    public void setShiroDAO(ShiroDAO shiroDAO) {
    this.shiroDAO = shiroDAO;
    }

    /**
    * 登录
    */
    public void doLogin(String username, String password) throws Exception {
    Subject currentUser = SecurityUtils.getSubject();
    if (!currentUser.isAuthenticated()) {
    UsernamePasswordToken token =
    new UsernamePasswordToken(username, password);
    token.setRememberMe(true);//是否记住用户
    try {
    currentUser.login(token);//执行登录
    } catch (UnknownAccountException uae) {
    throw new Exception(“账户不存在”);
    } catch (IncorrectCredentialsException ice) {
    throw new Exception(“密码不正确”);
    } catch (LockedAccountException lae) {
    throw new Exception("用户被锁定了 ");
    } catch (AuthenticationException ae) {
    ae.printStackTrace();
    throw new Exception(“未知错误”);
    }
    }
    }

    /**
    * 根据用户名查询密码
    */
    public String getPasswordByUserName(String username) {
    return shiroDAO.getPasswordByUserName(username);
    }

    /**
    * 查询用户所有权限
    */
    public List getPermissionByUserName(String username) {
    return shiroDAO.getPermissionByUserName(username);
    }
    }

    注:rememberMe后浏览器里会生成一个cookie:

    如果访问的路径,要求权限是user,所有使用过rememberMe的用户就都可以访问。但是它只是记录你登录过,不会记住你是谁以及你的权限信息。

    3.5 Controller

    package controller;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.servlet.ModelAndView;
    import service.ShiroService;

    @Controller
    public class ShiroController {
    @Autowired
    private ShiroService shiroService;

    @RequestMapping(“/gologin.html”)
    public String goLogin() {
    return “/login.jsp”;
    }

    @RequestMapping(“/login.html”)
    public ModelAndView login(String username, String password) {
    try {
    shiroService.doLogin(username, password);
    } catch (Exception e) {
    return new ModelAndView(“/error.jsp”, “msg”, e.getMessage());
    }
    return new ModelAndView(“/index.jsp”);
    }
    @RequestMapping(“/logout.html”)
    public String logout() {
    Subject currentUser = SecurityUtils.getSubject();
    currentUser.logout();
    return “/login.jsp”;
    }

    /**
    * 模拟不同的请求,在配置文件中对请求进行权限拦截
    */
    @RequestMapping(“/do{act}.html”)
    public ModelAndView get(@PathVariable String act) {
    //简化代码,省略数据库操作
    //通过页面上显示的信息查看请求是否被拦截
    return new ModelAndView(“/page.jsp”, “page”, act);
    }
    }

    3.6 自定义Realm

    package util;

    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import service.ShiroService;

    import java.util.List;

    public class MyShiroRealm extends AuthorizingRealm {
    private ShiroService shiroService;

    public void setShiroService(ShiroService shiroService) {
    this.shiroService = shiroService;
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo

    (PrincipalCollection principalCollection) {
    //根据自己的需求编写获取授权信息,这里简化代码获取了用户对应的所有权限
    String username =

    (String) principalCollection.fromRealm(getName()).iterator().next();
    if (username != null) {
    List perms = shiroService.getPermissionByUserName(username);
    if (perms != null && !perms.isEmpty()) {
    SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    for (String each : perms) {
    //将权限资源添加到用户信息中
    info.addStringPermission(each);
    }
    return info;
    }
    }
    return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo

    (AuthenticationToken authenticationToken) throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    // 通过表单接收的用户名,调用currentUser.login的时候执行
    String username = token.getUsername();
    if (username != null && !“”.equals(username)) {
    //查询密码
    String password = shiroService.getPasswordByUserName(username);
    if (password != null) {
    return new SimpleAuthenticationInfo(username, password, getName());
    }
    }
    return null;
    }
    }

    3.7 配置文件

    resources目录下的db.properties用于存放数据库配置信息:

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/数据库characterEncoding=utf-8
    jdbc.username=用户名
    jdbc.password=密码

    log4j.properties

    log4j.rootLogger=INFO, stdout,

    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

    WEB-INF目录下的applicationContext.xml:

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


    j d b c . d r i v e r " / > < p r o p e r t y n a m e " ˉ u r l " v a l u e " ˉ {jdbc.driver}"/> jdbc.driver"/><propertyname"ˉurl"value"ˉ{jdbc.url}”/>
    j d b c . u s e r n a m e " / > < p r o p e r t y n a m e " ˉ p a s s w o r d " v a l u e " ˉ {jdbc.username}"/> jdbc.username"/><propertyname"ˉpassword"value"ˉ{jdbc.password}”/>

















    /gologin.html = anon
    /login.html = anon
    /doadd.html = authc, perms[perm1,perm2]
    /doget.html = authc, perms[admin]
    /doupdate.html = authc, perms[perm1]
    /dodel.html = authc, perms[perm2]



    shiro过滤器过滤属性含义:

    securityManager:这个属性是必须的。

    loginUrl:没有登录的用户请求需要登录的页面时自动跳转到登录页面,不是必须的属性,不输入地址的话会自动寻找项目web项目的根目录下的”/login.jsp”页面。

    successUrl:登录成功默认跳转页面,不配置则跳转至”/”。如果登陆前点击的一个需要登录的页面,则在登录自动跳转到那个需要登录的页面。不跳转到此。

    unauthorizedUrl:没有权限默认跳转的页面

    其权限过滤器及配置释义:

    anon:

    例子/admins/**=anon没有参数,表示可以匿名使用。

    authc:

    例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数

    roles(角色):

    例子/admins/user/**=roles[admin],参数可以写多个,参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles[“admin,guest”],每个参数通过才算通过,相当于hasAllRoles()方法。

    perms(权限)

    例子/admins/user/**=perms[add],参数可以写多个,例如/admins/user/**=perms[“add, modify”],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

    rest:

    例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method],其中method为post,get,delete等。

    port:

    例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString

    是你访问的url里的?后面的参数。

    authcBasic:

    例如/admins/user/**=authcBasic没有参数.表示httpBasic认证

    ssl:

    例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https

    user:

    例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查

    springMVC-servlet.xml:

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



    web.xml:

    <xml version=“1.0” encoding=“UTF-8”>
    xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
    xsi:schemaLocation=“http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd”
    version=“3.0”>
    Archetype Created Web Application


    org.springframework.web.context.ContextLoaderListener


    gologin.html


    springMVC
    org.springframework.web.servlet.DispatcherServlet


    springMVC
    *.html


    shiroFilter
    org.springframework.web.filter.DelegatingFilterProxy


    shiroFilter
    *.html

    3.8 页面

    页面没有添加样式,将这些代码写在body中即可。

    login.jsp

    index.jsp

    add

    del

    update

    get

    page.jsp

    ${page}

    error.jsp

    ERROR:${msg}

    测试结果:

    登录成功:

    点击add、del、update可以看到对应的页面:

    点击get会报错,并且直接跳转到error.jsp,因为没有权限:

    3.9 动态配置过滤规则

    在实际开发中,url和对应的访问权限可能需要从数据库中读取,我们可以定义一个工具类从数据库中读取访问权限并传递给Shiro。

    package util;

    import org.apache.shiro.config.Ini;
    import org.apache.shiro.util.CollectionUtils;
    import org.apache.shiro.web.config.IniFilterChainResolverFactory;
    import org.springframework.beans.factory.FactoryBean;

    import java.text.MessageFormat;
    import java.util.HashMap;
    import java.util.LinkedHashSet;
    import java.util.Map;
    import java.util.Set;

    public class MyChainDefinitions implements FactoryBean {
    public static final String PREMISSION_STRING = “perms[{0}]”;
    public static final String ROLE_STRING = “roles[{0}]”;
    private String filterChainDefinitions;

    public void setFilterChainDefinitions(String filterChainDefinitions) {
    this.filterChainDefinitions = filterChainDefinitions;
    }

    @Override
    public Ini.Section getObject() throws Exception {
    //urls可以从数据库查出来,此处省略代码,直接模拟几条数据
    Set urls = new LinkedHashSet<>();
    urls.add(“/dotest1.html”);
    urls.add(“/dotest2.html”);
    //每个url对应的权限也可以从数据库中查出来,这里模拟几条数据
    Map permsMap = new HashMap<>();
    permsMap.put(“/dotest1.html”, new String[]{“perm1”, “admin”});
    permsMap.put(“/dotest2.html”, new String[]{“perm1”});

    //加载配置默认的过滤链
    Ini ini = new Ini();
    ini.load(filterChainDefinitions);
    Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
    if (CollectionUtils.isEmpty(section)) {
    section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
    }
    for (String url : urls) {
    String[] perms = permsMap.get(url);
    StringBuilder permFilters = new StringBuilder();
    for (int i = 0; i < perms.length; i++) {
    permFilters.append(perms[i]).append(“,”);
    }
    //去掉末尾的逗号
    String str = permFilters.substring(0, permFilters.length() - 1);
    //生成结果如:/dotest1.html = authc, perms[admin]
    section.put(url, MessageFormat.format(PREMISSION_STRING, str));
    }
    return section;
    }

    @Override
    public Class<> getObjectType() {
    return this.getClass();
    }

    @Override
    public boolean isSingleton() {
    return false;
    }
    }

    注意section中是以Map存放的数据,所以放入相同的key,后放的会覆盖先放的数据。

    修改spring的配置:



    /gologin.html = anon
    /login.html = anon
    /doadd.html = authc, perms[perm1,perm2]
    /doget.html = authc, perms[admin]
    /doupdate.html = authc, perms[perm1]
    /dodel.html = authc, perms[perm2]
    /logout.html=user


    将shiroFilter中的filterChainDefinitions替换掉:






    可以访问/dotest1.html和/dotest2.html查看拦截效果。

    3.10重写过滤器

    /doadd.html = authc,perms[perm1,perm2]

    shiro默认的拦截是要满足所有的条件,但有时我们只要满足其中的一个,用于拥有perm1或perm2任何一个条件就可以访问/doadd.html。这时我们可以重写过滤器,将and变成or

    package util;

    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;

    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.filter.authz.AuthorizationFilter;

    public class MyShiroPermFilter extends AuthorizationFilter {
    @Override
    protected boolean isAccessAllowed
    (ServletRequest req, ServletResponse resp, Object mappedValue)

    throws Exception {
    Subject subject = getSubject(req, resp);
    String[] permsArray = (String[]) mappedValue;

    if (permsArray == null || permsArray.length == 0) { //没有权限限制
    return true;
    }
    for (int i = 0; i < permsArray.length; i++) {
    //如果是角色,就是subject.hasRole()
    //若当前用户是permsArray中的任何一个,则有权限访问
    if (subject.isPermitted(permsArray[i])) {
    return true;
    }
    }
    return false;
    }
    }

    此处需要引入servlet的jar包:


    javax.servlet
    javax.servlet-api
    3.1.0

    在spring中的配置,在id=”shiroFilter”的bean中加入过滤拦截:













    3.11rememberMe属性

    rememberMe可以在浏览器中设置cookie,在spring配置中可以设置cookie的属性,如过期时间、cookie名字、加密的秘钥等:







    在securityManager中加入rememberMe中加入配置:



    HttpOnly属性:

    浏览器中通过document.cookie可以获取cookie属性,设置了HttpOnly=true,在脚本中就不能的到cookie了。可以避免cookie被盗用。

  • 相关阅读:
    产品思维训练 | 你的项目总是不能按期上线,你会如何解决?
    人工智能教程(一)
    华为认证大数据是什么?华为认证大数据有用吗?
    延迟任务多种实现姿势--中
    智能聊天机器人如何帮助跨境电商卖家提高成交转化率?
    重上吹麻滩——段芝堂创始人翟立冬游记
    vscode基于cmake结果调试运行
    c++ 11 auto、decltype比较
    Docker的数据卷
    c++(8.29)auto关键字,lambda表达式,数据类型转换,标准模板库,list,文件操作+Xmind
  • 原文地址:https://blog.csdn.net/m0_61083409/article/details/126564811