当前位置: 代码网 > it编程>编程语言>Java > SpringBoot Shiro权限管理方式

SpringBoot Shiro权限管理方式

2025年03月26日 Java 我要评论
springboot shiro权限管理本来是打算接着写关于数据库方面,集成mybatis的,刚好赶上朋友问到shiro权限管理,就先总结下发出来了。使用shiro之前用在spring mvc中,是通

springboot shiro权限管理

本来是打算接着写关于数据库方面,集成mybatis的,刚好赶上朋友问到shiro权限管理,就先总结下发出来了。

使用shiro之前用在spring mvc中,是通过xml文件进行配置。

既然现在在写spring boot的帖子,就将shiro应用到spring boot中,我本地已经完成了springboot使用shiro的实例,将配置方法共享一下。

先简单介绍一下shiro,对于没有用过shiro的朋友,也算是做个简介吧。

shiro是apache下的一个开源项目,我们称之为apache shiro。它是一个很易用与java项目的的安全框架,提供了认证、授权、加密、会话管理,与 spring security 一样都是做一个权限的安全框架,但是与spring security 相比,在于 shiro 使用了比较简单易懂易于使用的授权方式。

apache shiro 的三大核心组件

subject 当前用户操作securitymanager 用于管理所有的subjectrealms 用于进行权限信息的验证,也是我们需要自己实现的。

我们需要实现realms的authentication 和 authorization。其中 authentication 是用来验证用户身份,authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。

apache shiro 核心通过 filter 来实现,就好像springmvc 通过dispachservlet 来主控制一样。

既然是使用 filter 一般也就能猜到,是通过url规则来进行过滤和权限校验,所以我们需要定义一系列关于url的规则和访问权限。

另外我们可以通过shiro 提供的会话管理来获取session中的信息。shiro 也提供了缓存支持,使用 cachemanager 来管理。

官方网站:http://shiro.apache.org/

完整架构图:

下面我们通过代码实战来看下spring boot 中应用shiro:

1、创建数据库表

表(t_permission)
    id  permissionname  role_id  
------  --------------  ---------
     1  add                     2
     2  del                     1
     3  update                  2
     4  query                   3
     5  user:query              1
     6  user:edit               2

表(t_role)
    id  rolename  
------  ----------
     1  admin     
     2  manager   
     3  normal    

表(t_user)
    id  username  password  
------  --------  ----------
     1  tom       123456    
     2  jack      123456    
     3  rose      123456  

表(t_user_role)
user_id  role_id  
-------  ---------
      1          1
      1          3
      2          2
      2          3
      3          3

看截图,上面3张表是我测试别的用的,可以忽略。

下面是,数据库脚本和测试数据。

/*
sqlyog ultimate v10.00 beta1
mysql - 5.5.28 : database - test
*********************************************************************
*/


/*!40101 set names utf8 */;

/*!40101 set sql_mode=''*/;

/*!40014 set @old_unique_checks=@@unique_checks, unique_checks=0 */;
/*!40014 set @old_foreign_key_checks=@@foreign_key_checks, foreign_key_checks=0 */;
/*!40101 set @old_sql_mode=@@sql_mode, sql_mode='no_auto_value_on_zero' */;
/*!40111 set @old_sql_notes=@@sql_notes, sql_notes=0 */;
create database /*!32312 if not exists*/`test` /*!40100 default character set utf8 */;

use `test`;

/*table structure for table `t_permission` */

drop table if exists `t_permission`;

create table `t_permission` (
  `id` int(11) not null auto_increment,
  `permissionname` varchar(32) default null,
  `role_id` int(11) default null,
  key `id` (`id`)
) engine=innodb auto_increment=7 default charset=utf8;

/*data for the table `t_permission` */

insert  into `t_permission`(`id`,`permissionname`,`role_id`) values (1,'add',2),(2,'del',1),(3,'update',2),(4,'query',3),(5,'user:query',1),(6,'user:edit',2);

/*table structure for table `t_role` */

drop table if exists `t_role`;

create table `t_role` (
  `id` int(11) not null auto_increment,
  `rolename` varchar(32) default null,
  key `id` (`id`)
) engine=innodb auto_increment=4 default charset=utf8;

/*data for the table `t_role` */

insert  into `t_role`(`id`,`rolename`) values (1,'admin'),(2,'manager'),(3,'normal');

/*table structure for table `t_user` */

drop table if exists `t_user`;

create table `t_user` (
  `id` int(11) not null auto_increment,
  `username` varchar(32) default null,
  `password` varchar(32) default null,
  primary key (`id`)
) engine=innodb auto_increment=4 default charset=utf8;

/*data for the table `t_user` */

insert  into `t_user`(`id`,`username`,`password`) values (1,'tom','123456'),(2,'jack','123456'),(3,'rose','123456');

/*table structure for table `t_user_role` */

drop table if exists `t_user_role`;

create table `t_user_role` (
  `user_id` int(11) default null,
  `role_id` int(11) default null
) engine=innodb default charset=utf8;

/*data for the table `t_user_role` */

insert  into `t_user_role`(`user_id`,`role_id`) values (1,1),(1,3),(2,2),(2,3),(3,3);

/*!40101 set sql_mode=@old_sql_mode */;
/*!40014 set foreign_key_checks=@old_foreign_key_checks */;
/*!40014 set unique_checks=@old_unique_checks */;
/*!40111 set sql_notes=@old_sql_notes */;

2、创建对应实体类

  • user.java
package org.springboot.sample.entity;

import java.util.hashset;
import java.util.list;
import java.util.set;

import javax.persistence.entity;
import javax.persistence.fetchtype;
import javax.persistence.generatedvalue;
import javax.persistence.generationtype;
import javax.persistence.id;
import javax.persistence.joincolumn;
import javax.persistence.jointable;
import javax.persistence.manytomany;
import javax.persistence.table;
import javax.persistence.transient;

import org.hibernate.validator.constraints.notempty;

/**
 * 用户
 *
 * @author 单红宇(365384722)
 * @myblog http://blog.csdn.net/catoop/
 * @create 2016年1月13日
 */
@entity
@table(name = "t_user")
public class user {

    @id
    @generatedvalue(strategy = generationtype.identity)
    private integer id;
    @notempty(message = "用户名不能为空")
    private string username;
    @notempty(message = "密码不能为空")
    private string password;    
    @manytomany(fetch=fetchtype.eager)
    @jointable(name = "t_user_role", joincolumns = { @joincolumn(name = "user_id") }, inversejoincolumns = {
            @joincolumn(name = "role_id") })
    private list<role> rolelist;// 一个用户具有多个角色

    public user() {
        super();
    }

    public user(string username, string password) {
        super();
        this.username = username;
        this.password = password;
    }

    // 省略 get set 方法

    @transient
    public set<string> getrolesname() {
        list<role> roles = getrolelist();
        set<string> set = new hashset<string>();
        for (role role : roles) {
            set.add(role.getrolename());
        }
        return set;
    }

}
  • role.java
package org.springboot.sample.entity;

import java.util.arraylist;
import java.util.list;

import javax.persistence.entity;
import javax.persistence.fetchtype;
import javax.persistence.generatedvalue;
import javax.persistence.generationtype;
import javax.persistence.id;
import javax.persistence.joincolumn;
import javax.persistence.jointable;
import javax.persistence.manytomany;
import javax.persistence.onetomany;
import javax.persistence.table;
import javax.persistence.transient;

/**
 * 角色(管理员,普通用户等)
 *
 * @author   单红宇(365384722)
 * @myblog  http://blog.csdn.net/catoop/
 * @create    2016年1月13日
 */
@entity
@table(name = "t_role")
public class role {

    @id
    @generatedvalue(strategy = generationtype.identity)
    private integer id;
    private string rolename;
    @onetomany(mappedby = "role", fetch=fetchtype.eager)
    private list<permission> permissionlist;// 一个角色对应多个权限
    @manytomany
    @jointable(name = "t_user_role", joincolumns = { @joincolumn(name = "role_id") }, inversejoincolumns = {
            @joincolumn(name = "user_id") })
    private list<user> userlist;// 一个角色对应多个用户

    // 省略 get set 方法

    @transient
    public list<string> getpermissionsname() {
        list<string> list = new arraylist<string>();
        list<permission> perlist = getpermissionlist();
        for (permission per : perlist) {
            list.add(per.getpermissionname());
        }
        return list;
    }
}
  • permission.java
package org.springboot.sample.entity;

import javax.persistence.entity;
import javax.persistence.generatedvalue;
import javax.persistence.generationtype;
import javax.persistence.id;
import javax.persistence.joincolumn;
import javax.persistence.manytoone;
import javax.persistence.table;

/**
 * 权限(增删改查等)
 *
 * @author 单红宇(365384722)
 * @myblog http://blog.csdn.net/catoop/
 * @create 2016年1月13日
 */
@entity
@table(name = "t_permission")
public class permission {

    @id
    @generatedvalue(strategy = generationtype.identity)
    private integer id;
    private string permissionname;

    @manytoone
    @joincolumn(name = "role_id")
    private role role;// 一个权限对应一个角色

    // 省略 get set

}

3、shiro 配置,相当于springmvc 中的xml配置

  • shiroconfiguration.java
package org.springboot.sample.config;

import java.util.linkedhashmap;
import java.util.map;

import org.apache.shiro.cache.ehcache.ehcachemanager;
import org.apache.shiro.spring.lifecyclebeanpostprocessor;
import org.apache.shiro.spring.security.interceptor.authorizationattributesourceadvisor;
import org.apache.shiro.spring.web.shirofilterfactorybean;
import org.apache.shiro.web.mgt.defaultwebsecuritymanager;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springboot.sample.dao.iscoredao;
import org.springboot.sample.security.myshirorealm;
import org.springboot.sample.service.studentservice;
import org.springframework.aop.framework.autoproxy.defaultadvisorautoproxycreator;
import org.springframework.boot.context.embedded.filterregistrationbean;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.web.filter.delegatingfilterproxy;

/**
 * shiro 配置
 *
 * @author   单红宇(365384722)
 * @myblog  http://blog.csdn.net/catoop/
 * @create    2016年1月13日
 */
@configuration
public class shiroconfiguration {

    private static final logger logger = loggerfactory.getlogger(shiroconfiguration.class);

    @bean
    public ehcachemanager getehcachemanager() {  
        ehcachemanager em = new ehcachemanager();  
        em.setcachemanagerconfigfile("classpath:ehcache-shiro.xml");  
        return em;  
    }  

    @bean(name = "myshirorealm")
    public myshirorealm myshirorealm(ehcachemanager cachemanager) {  
        myshirorealm realm = new myshirorealm(); 
        realm.setcachemanager(cachemanager);
        return realm;
    }  

    /**
     * 注册delegatingfilterproxy(shiro)
     * 集成shiro有2种方法:
     * 1. 按这个方法自己组装一个filterregistrationbean(这种方法更为灵活,可以自己定义urlpattern,
     * 在项目使用中你可能会因为一些很但疼的问题最后采用它, 想使用它你可能需要看官网或者已经很了解shiro的处理原理了)
     * 2. 直接使用shirofilterfactorybean(这种方法比较简单,其内部对shirofilter做了组装工作,无法自己定义urlpattern,
     * 默认拦截 /*)
     *
     * @param dispatcherservlet
     * @return
     * @author shanhy
     * @create  2016年1月13日
     */
//  @bean
//  public filterregistrationbean filterregistrationbean() {
//      filterregistrationbean filterregistration = new filterregistrationbean();
//      filterregistration.setfilter(new delegatingfilterproxy("shirofilter"));
//      //  该值缺省为false,表示生命周期由springapplicationcontext管理,设置为true则表示由servletcontainer管理  
//      filterregistration.addinitparameter("targetfilterlifecycle", "true");
//      filterregistration.setenabled(true);
//      filterregistration.addurlpatterns("/*");// 可以自己灵活的定义很多,避免一些根本不需要被shiro处理的请求被包含进来
//      return filterregistration;
//  }

    @bean(name = "lifecyclebeanpostprocessor")
    public lifecyclebeanpostprocessor getlifecyclebeanpostprocessor() {
        return new lifecyclebeanpostprocessor();
    }

    @bean
    public defaultadvisorautoproxycreator getdefaultadvisorautoproxycreator() {
        defaultadvisorautoproxycreator daap = new defaultadvisorautoproxycreator();
        daap.setproxytargetclass(true);
        return daap;
    }

    @bean(name = "securitymanager")
    public defaultwebsecuritymanager getdefaultwebsecuritymanager(myshirorealm myshirorealm) {
        defaultwebsecuritymanager dwsm = new defaultwebsecuritymanager();
        dwsm.setrealm(myshirorealm);
//      <!-- 用户授权/认证信息cache, 采用ehcache 缓存 --> 
        dwsm.setcachemanager(getehcachemanager());
        return dwsm;
    }

    @bean
    public authorizationattributesourceadvisor getauthorizationattributesourceadvisor(defaultwebsecuritymanager securitymanager) {
        authorizationattributesourceadvisor aasa = new authorizationattributesourceadvisor();
        aasa.setsecuritymanager(securitymanager);
        return aasa;
    }

    /**
     * 加载shirofilter权限控制规则(从数据库读取然后配置)
     *
     * @author shanhy
     * @create  2016年1月14日
     */
    private void loadshirofilterchain(shirofilterfactorybean shirofilterfactorybean, studentservice stuservice, iscoredao scoredao){
        /// 下面这些规则配置最好配置到配置文件中 ///
        map<string, string> filterchaindefinitionmap = new linkedhashmap<string, string>();
        // authc:该过滤器下的页面必须验证后才能访问,它是shiro内置的一个拦截器org.apache.shiro.web.filter.authc.formauthenticationfilter
        filterchaindefinitionmap.put("/user", "authc");// 这里为了测试,只限制/user,实际开发中请修改为具体拦截的请求规则
        // anon:它对应的过滤器里面是空的,什么都没做
        logger.info("##################从数据库读取权限规则,加载到shirofilter中##################");
        filterchaindefinitionmap.put("/user/edit/**", "authc,perms[user:edit]");// 这里为了测试,固定写死的值,也可以从数据库或其他配置中读取

        filterchaindefinitionmap.put("/login", "anon");
        filterchaindefinitionmap.put("/**", "anon");//anon 可以理解为不拦截

        shirofilterfactorybean.setfilterchaindefinitionmap(filterchaindefinitionmap);
    }

    /**
     * shirofilter<br/>
     * 注意这里参数中的 studentservice 和 iscoredao 只是一个例子,因为我们在这里可以用这样的方式获取到相关访问数据库的对象,
     * 然后读取数据库相关配置,配置到 shirofilterfactorybean 的访问规则中。实际项目中,请使用自己的service来处理业务逻辑。
     *
     * @param myshirorealm
     * @param stuservice
     * @param scoredao
     * @return
     * @author shanhy
     * @create  2016年1月14日
     */
    @bean(name = "shirofilter")
    public shirofilterfactorybean getshirofilterfactorybean(defaultwebsecuritymanager securitymanager, studentservice stuservice, iscoredao scoredao) {

        shirofilterfactorybean shirofilterfactorybean = new mshirofilterfactorybean();
        // 必须设置 securitymanager  
        shirofilterfactorybean.setsecuritymanager(securitymanager);
        // 如果不设置默认会自动寻找web工程根目录下的"/login.jsp"页面
        shirofilterfactorybean.setloginurl("/login");
        // 登录成功后要跳转的连接
        shirofilterfactorybean.setsuccessurl("/user");
        shirofilterfactorybean.setunauthorizedurl("/403");

        loadshirofilterchain(shirofilterfactorybean, stuservice, scoredao);
        return shirofilterfactorybean;
    }

}
/**
 * 继承 shirofilterfactorybean 处理拦截资源文件问题。
 *
 * @author   单红宇(365384722)
 * @myblog  http://blog.csdn.net/catoop/
 * @create    2016年3月8日
 */
public class mshirofilterfactorybean extends shirofilterfactorybean {

    // 对shirofilter来说,需要直接忽略的请求
    private set<string> ignoreext;

    public mshirofilterfactorybean() {
        super();
        ignoreext = new hashset<>();
        ignoreext.add(".jpg");
        ignoreext.add(".png");
        ignoreext.add(".gif");
        ignoreext.add(".bmp");
        ignoreext.add(".js");
        ignoreext.add(".css");
    }

    @override
    protected abstractshirofilter createinstance() throws exception {

        securitymanager securitymanager = getsecuritymanager();
        if (securitymanager == null) {
            string msg = "securitymanager property must be set.";
            throw new beaninitializationexception(msg);
        }

        if (!(securitymanager instanceof websecuritymanager)) {
            string msg = "the security manager does not implement the websecuritymanager interface.";
            throw new beaninitializationexception(msg);
        }

        filterchainmanager manager = createfilterchainmanager();

        pathmatchingfilterchainresolver chainresolver = new pathmatchingfilterchainresolver();
        chainresolver.setfilterchainmanager(manager);

        return new mspringshirofilter((websecuritymanager) securitymanager, chainresolver);
    }

    private final class mspringshirofilter extends abstractshirofilter {

        protected mspringshirofilter(websecuritymanager websecuritymanager, filterchainresolver resolver) {
            super();
            if (websecuritymanager == null) {
                throw new illegalargumentexception("websecuritymanager property cannot be null.");
            }
            setsecuritymanager(websecuritymanager);
            if (resolver != null) {
                setfilterchainresolver(resolver);
            }
        }

        @override
        protected void dofilterinternal(servletrequest servletrequest, servletresponse servletresponse,
                filterchain chain) throws servletexception, ioexception {
            httpservletrequest request = (httpservletrequest)servletrequest;
            string str = request.getrequesturi().tolowercase();
            // 因为shirofilter 拦截所有请求(在上面我们配置了urlpattern 为 * ,当然你也可以在那里精确的添加要处理的路径,这样就不需要这个类了),而在每次请求里面都做了session的读取和更新访问时间等操作,这样在集群部署session共享的情况下,数量级的加大了处理量负载。
            // 所以我们这里将一些能忽略的请求忽略掉。
            // 当然如果你的集群系统使用了动静分离处理,静态资料的请求不会到filter这个层面,便可以忽略。
            boolean flag = true;
            int idx = 0;
            if(( idx = str.indexof(".")) > 0){
                str = str.substring(idx);
                if(ignoreext.contains(str.tolowercase()))
                    flag = false;
            }
            if(flag){
                super.dofilterinternal(servletrequest, servletresponse, chain);
            }else{
                chain.dofilter(servletrequest, servletresponse);
            }
        }

    }
}

其中的 ehcache-shiro.xml 在 src/main/resources 下面,内容为:

<?xml version="1.0" encoding="utf-8"?>
<ehcache updatecheck="false" name="shirocache">

    <defaultcache
            maxelementsinmemory="10000"
            eternal="false"
            timetoidleseconds="120"
            timetoliveseconds="120"
            overflowtodisk="false"
            diskpersistent="false"
            diskexpirythreadintervalseconds="120"
            />
</ehcache>

4、继承 authorizingrealm 实现认证和授权2个方法

  • myshirorealm.java
package org.springboot.sample.security;

import java.util.list;

import org.apache.commons.lang3.builder.reflectiontostringbuilder;
import org.apache.commons.lang3.builder.tostringstyle;
import org.apache.shiro.authc.authenticationexception;
import org.apache.shiro.authc.authenticationinfo;
import org.apache.shiro.authc.authenticationtoken;
import org.apache.shiro.authc.simpleauthenticationinfo;
import org.apache.shiro.authc.usernamepasswordtoken;
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 org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springboot.sample.dao.iuserdao;
import org.springboot.sample.entity.role;
import org.springboot.sample.entity.user;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.service;
import org.springframework.transaction.annotation.transactional;

/**
 * myshirorealm
 *
 * @author   单红宇(365384722)
 * @myblog  http://blog.csdn.net/catoop/
 * @create    2016年1月13日
 */
public class myshirorealm extends authorizingrealm{

    private static final logger logger = loggerfactory.getlogger(myshirorealm.class);

    @autowired
    private iuserdao userdao; 

    /**
     * 权限认证,为当前登录的subject授予角色和权限 
     * @see 经测试:本例中该方法的调用时机为需授权资源被访问时 
     * @see 经测试:并且每次访问需授权资源时都会执行该方法中的逻辑,这表明本例中默认并未启用authorizationcache 
     * @see 经测试:如果连续访问同一个url(比如刷新),该方法不会被重复调用,shiro有一个时间间隔(也就是cache时间,在ehcache-shiro.xml中配置),超过这个时间间隔再刷新页面,该方法会被执行
     */
    @override
    protected authorizationinfo dogetauthorizationinfo(principalcollection principalcollection) {
        logger.info("##################执行shiro权限认证##################");
        //获取当前登录输入的用户名,等价于(string) principalcollection.fromrealm(getname()).iterator().next();
        string loginname = (string)super.getavailableprincipal(principalcollection); 
        //到数据库查是否有此对象
        user user=userdao.findbyname(loginname);// 实际项目中,这里可以根据实际情况做缓存,如果不做,shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
        if(user!=null){
            //权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
            simpleauthorizationinfo info=new simpleauthorizationinfo();
            //用户的角色集合
            info.setroles(user.getrolesname());
            //用户的角色对应的所有权限,如果只使用角色定义访问权限,下面的四行可以不要
            list<role> rolelist=user.getrolelist();
            for (role role : rolelist) {
                info.addstringpermissions(role.getpermissionsname());
            }
            // 或者按下面这样添加
            //添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色    
//            simpleauthorinfo.addrole("admin");  
            //添加权限  
//            simpleauthorinfo.addstringpermission("admin:manage");  
//            logger.info("已为用户[mike]赋予了[admin]角色和[admin:manage]权限");
            return info;
        }
        // 返回null的话,就会导致任何用户访问被拦截的请求时,都会自动跳转到unauthorizedurl指定的地址
        return null;
    }

    /**
     * 登录认证
     */
    @override
    protected authenticationinfo dogetauthenticationinfo(
            authenticationtoken authenticationtoken) throws authenticationexception {
        //usernamepasswordtoken对象用来存放提交的登录信息
        usernamepasswordtoken token=(usernamepasswordtoken) authenticationtoken;

        logger.info("验证当前subject时获取到token为:" + reflectiontostringbuilder.tostring(token, tostringstyle.multi_line_style)); 

        //查出是否有此用户
        user user=userdao.findbyname(token.getusername());
        if(user!=null){
            // 若存在,将此用户存放到登录认证info中,无需自己做密码对比,shiro会为我们进行密码对比校验
            return new simpleauthenticationinfo(user.getusername(), user.getpassword(), getname());
        }
        return null;
    }
}

注意:其中 userdao.findbyname 这个代码就不贴上了,也没啥可贴的,根据姓名查询一个对象而已。

5、编写测试的 controller 和测试 jsp 页面

  • shirocontroller.java
package org.springboot.sample.controller;

import java.util.map;

import javax.validation.valid;

import org.apache.shiro.securityutils;
import org.apache.shiro.authc.authenticationexception;
import org.apache.shiro.authc.excessiveattemptsexception;
import org.apache.shiro.authc.incorrectcredentialsexception;
import org.apache.shiro.authc.lockedaccountexception;
import org.apache.shiro.authc.unknownaccountexception;
import org.apache.shiro.authc.usernamepasswordtoken;
import org.apache.shiro.subject.subject;
import org.slf4j.logger;
import org.slf4j.loggerfactory;
import org.springboot.sample.dao.iuserdao;
import org.springboot.sample.entity.user;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.stereotype.controller;
import org.springframework.ui.model;
import org.springframework.validation.bindingresult;
import org.springframework.web.bind.annotation.pathvariable;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.requestmethod;
import org.springframework.web.servlet.mvc.support.redirectattributes;

/**
 * shiro测试controller
 *
 * @author   单红宇(365384722)
 * @myblog  http://blog.csdn.net/catoop/
 * @create    2016年1月13日
 */
@controller
public class shirocontroller {

    private static final logger logger = loggerfactory.getlogger(shirocontroller.class);

    @autowired
    private iuserdao userdao;

    @requestmapping(value="/login",method=requestmethod.get)
    public string loginform(model model){
        model.addattribute("user", new user());
        return "login";
    }

    @requestmapping(value="/login",method=requestmethod.post)
    public string login(@valid user user,bindingresult bindingresult,redirectattributes redirectattributes){
        if(bindingresult.haserrors()){
            return "login";
        }

        string username = user.getusername();
        usernamepasswordtoken token = new usernamepasswordtoken(user.getusername(), user.getpassword());
        //获取当前的subject  
        subject currentuser = securityutils.getsubject();  
        try {  
            //在调用了login方法后,securitymanager会收到authenticationtoken,并将其发送给已配置的realm执行必须的认证检查  
            //每个realm都能在必要时对提交的authenticationtokens作出反应  
            //所以这一步在调用login(token)方法时,它会走到myrealm.dogetauthenticationinfo()方法中,具体验证方式详见此方法  
            logger.info("对用户[" + username + "]进行登录验证..验证开始");  
            currentuser.login(token);  
            logger.info("对用户[" + username + "]进行登录验证..验证通过");  
        }catch(unknownaccountexception uae){  
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,未知账户");  
            redirectattributes.addflashattribute("message", "未知账户");  
        }catch(incorrectcredentialsexception ice){  
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误的凭证");  
            redirectattributes.addflashattribute("message", "密码不正确");  
        }catch(lockedaccountexception lae){  
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,账户已锁定");  
            redirectattributes.addflashattribute("message", "账户已锁定");  
        }catch(excessiveattemptsexception eae){  
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,错误次数过多");  
            redirectattributes.addflashattribute("message", "用户名或密码错误次数过多");  
        }catch(authenticationexception ae){  
            //通过处理shiro的运行时authenticationexception就可以控制用户登录失败或密码错误时的情景  
            logger.info("对用户[" + username + "]进行登录验证..验证未通过,堆栈轨迹如下");  
            ae.printstacktrace();  
            redirectattributes.addflashattribute("message", "用户名或密码不正确");  
        }  
        //验证是否登录成功  
        if(currentuser.isauthenticated()){  
            logger.info("用户[" + username + "]登录认证通过(这里可以进行一些认证通过后的一些系统参数初始化操作)");  
            return "redirect:/user";
        }else{  
            token.clear();  
            return "redirect:/login";
        }  
    }

    @requestmapping(value="/logout",method=requestmethod.get)  
    public string logout(redirectattributes redirectattributes ){ 
        //使用权限管理工具进行用户的退出,跳出登录,给出提示信息
        securityutils.getsubject().logout();  
        redirectattributes.addflashattribute("message", "您已安全退出");  
        return "redirect:/login";
    } 

    @requestmapping("/403")
    public string unauthorizedrole(){
        logger.info("------没有权限-------");
        return "403";
    }

    @requestmapping("/user")
    public string getuserlist(map<string, object> model){
        model.put("userlist", userdao.getlist());
        return "user";
    }

    @requestmapping("/user/edit/{userid}")
    public string getuserlist(@pathvariable int userid){
        logger.info("------进入用户信息修改-------");
        return "user_edit";
    }
}
  • login.jsp
<%@ page language="java" import="java.util.*" pageencoding="utf-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html>
<head>
<title>login</title>
</head>

<body>
    <h1>登录页面----${message }</h1>
    <img alt="" src="${pagecontext.request.contextpath }/pic.jpg">
    <form:form action="${pagecontext.request.contextpath }/login"
        commandname="user" method="post">
        用户名:<form:input path="username" />
        <form:errors path="username" cssclass="error" />
        <br />
        密码:<form:password path="password" />
        <form:errors path="password" cssclass="error" />
        <br />
        <form:button name="button">提交</form:button>
    </form:form>
</body>
</html>
  • user.jsp
<%@ page language="java" import="java.util.*" pageencoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html>
  <head>
    <title>用户列表</title>
  </head>
  <body>
    <h1>${message }</h1>
    <h1>用户列表--<a href="${pagecontext.request.contextpath }/logout" rel="external nofollow" >退出登录</a>    </h1>
    <h2>权限列表</h2>
    <shiro:authenticated>用户已经登录显示此内容<br/></shiro:authenticated><br/>
    <shiro:hasrole name="manager">manager角色登录显示此内容<br/></shiro:hasrole>
    <shiro:hasrole name="admin">admin角色登录显示此内容<br/></shiro:hasrole>
    <shiro:hasrole name="normal">normal角色登录显示此内容<br/></shiro:hasrole><br/>

    <shiro:hasanyroles name="manager,admin">manager or admin 角色用户登录显示此内容<br/></shiro:hasanyroles><br/>
    <shiro:principal/>-显示当前登录用户名<br/><br/>
    <shiro:haspermission name="add">add权限用户显示此内容<br/></shiro:haspermission>
    <shiro:haspermission name="user:query">user:query权限用户显示此内容<br/></shiro:haspermission>
    <shiro:lackspermission name="user:query">不具有user:query权限的用户显示此内容 <br/></shiro:lackspermission>

    <br/>所有用户列表:<br/>
    <ul>
        <c:foreach items="${userlist }" var="user">
            <li>用户名:${user.username }----密码:${user.password }----<a href="${pagecontext.request.contextpath }/user/edit/${user.id}" rel="external nofollow" >修改用户(测试根据不同用户可访问权限不同,本例tom无权限,jack有权限)</a></li>
        </c:foreach>
    </ul>
    <img alt="" src="${pagecontext.request.contextpath }/pic.jpg">
    <script type="text/javascript" src="${pagecontext.request.contextpath }/webjarslocator/jquery/jquery.js"></script>
  </body>
</html>
  • user_edit.jsp
<%@ page language="java" import="java.util.*" pageencoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html>
  <head>
    <title>用户信息 - 修改</title>
  </head>
  <body>
    <h2>修改用户信息页面</h2><br/>
    <a href="${pagecontext.request.contextpath }/user" rel="external nofollow" >返回用户列表</a>
  </body>
</html>
  • 403.jsp
<%@ page language="java" import="java.util.*" pageencoding="utf-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!doctype html public "-//w3c//dtd html 4.01 transitional//en">
<html>
  <head>
    <title>权限错误</title>
  </head>

  <body>
    <h1>对不起,您没有权限请求此连接!</h1>
    <img alt="" src="${pagecontext.request.contextpath }/pic.jpg">

  </body>
</html>

其中的pic.jpg 是测试代码遗留的,没有任何用处。关于 controller 和 jsp 页面本文不做介绍,关于spring boot 使用controller 和 jsp ,前面已经有文章介绍。

启动服务后访问 http://localhost:8080/myspringboot/user 会自动跳到 login 页面。

登录成功后,会打开 user 页面(关于默认登录页、成功成功url、没有权限url,在 shiroconfiguration 中已经配置)。

在 user 页面上,不同用户会根据权限不同显示不同的内容,下面的修改操作也已经有文字说明,更换账号测试便知。

然后我们在实际项目中:不但要在页面上控制不同权限隐藏或将某些操作设置为不可用状态,还要在实际上控制那个操作背后的请求是真的不可以使用的。(例如:页面上的修改按钮已经灰化了,而我知道了修改按钮正常情况下点击会触发的请求,此时我直接模拟这个修改请求,应当是没有权限的才对,这样才算是真正的控制了权限。)

附:

filter chain定义说明

1、一个url可以配置多个filter,使用逗号分隔

2、当设置多个过滤器时,全部验证通过,才视为通过

3、部分过滤器可指定参数,如perms,roles

shiro内置的filterchain

filter nameclass
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

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com