Shiro与SSM框架整合,实现系统权限控制

本文主要讲解Shiro安全框架与SSM(SpringMVC/Spring/Mybatis)框架整合,实现系统的权限控制。
本文建立在SSM框架的基础上,默认已经搭建了SSM框架。具体的SSM框架搭建,请参考文章“SSM框架搭建”,这里不做赘述。
Shiro安全框架与SSM整合的示例代码,github地址:https://github.com/1287642889/Shiro_SSM

前期准备工作:
1、新建Maven工程,并搭建SSM框架,项目结构如下所示:


image.png

2、pom.xml具体如下:

<!--Spring依赖-->
  <dependencies>
    <!--Spring依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aop</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-tx</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.0.0.RELEASE</version>
    </dependency>


    <!--mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.5</version>
    </dependency>

    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.1</version>
    </dependency>

    <!--shiro依赖-->
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.3.2</version>
    </dependency>


    <!--sqlserver数据库依赖-->
    <dependency>
      <groupId>com.microsoft.sqlserver</groupId>
      <artifactId>sqljdbc4</artifactId>
      <version>4.0</version>
    </dependency>

    <!--c3p0数据库连接池-->
    <dependency>
      <groupId>com.mchange</groupId>
      <artifactId>c3p0</artifactId>
      <version>0.9.5.2</version>
    </dependency>

    <!--jsp依赖-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.0</version>
      <scope>provided</scope>
    </dependency>

    <!--jstl标签库-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>

    <!--fileupload文件下载-->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.3.2</version>
    </dependency>


    <!--junit依赖-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
      </plugin>
    </plugins>
  </build>

3、数据库中新建用户表、角色表、资源表、用户角色表、角色资源表这5张表,在Users表中添加wzf、a、b三个用户;在Role表中添加admin、role1、role2三个角色,在Permission表中添加/student/add、/student/delete、/student/update、/student/select四个资源权限;并在相应的关系表中添加对应的数据。
本系统的权限分配情况是:wzf用户是admin角色,拥有所有的资源权限;a用户是role1角色,拥有除perms[student:delete]外的所有资源权限;b用户是role2角色,只拥有perms[student:select]资源权限。
数据库表结构和主外键关系具体如下:


image.png

在SSM框架的基础上整合Shiro功能。Shiro主要有Filter、Realm构成,主要代码文件如下:
1、配置ShiroConfiguration过滤器链,代码如下:

@Configuration
public class ShiroConfiguration {
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        System.out.println("ShiroConfiguration.shiroFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置SecuritManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 拦截器
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        filterChainDefinitionMap.put("/","anon");
        filterChainDefinitionMap.put("/login","anon");
        filterChainDefinitionMap.put("/student/add", "perms[/student/add]");
        filterChainDefinitionMap.put("/student/delete", "perms[/student/delete]");
        filterChainDefinitionMap.put("/student/update", "perms[/student/update]");
        filterChainDefinitionMap.put("/student/select", "perms[/student/select]");
        // 配置退出过滤器,其中的具体代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        // <!-- 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
        filterChainDefinitionMap.put("/**", "authc");

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/");
        // 登录成功后要跳转的链接
        //shiroFilterFactoryBean.setSuccessUrl("");
        // 未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }

    @Bean
    public MyRealm myShiroRealm(){
        MyRealm userRealm = new MyRealm();
        return userRealm;
    }

    //开启shiro aop注解支持.
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

2、自定义MyRealm代码如下:

public class MyRealm extends AuthorizingRealm {

    @Autowired
    private UsersService usersService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String)principals.getPrimaryPrincipal();
//        Users user = userService.findByUserName(username);
//        user.setLocked(true);   //登录成功后锁定用户
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();

        //根据用户名查找对应的角色集合
        authorizationInfo.setRoles(usersService.findRoles(username));
        //根据用户名查找对应的资源集合
        authorizationInfo.setStringPermissions(usersService.findPermissions(username));

        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String)token.getPrincipal();

        //根据用户名查找用户
        Users user = usersService.findByUserName(username);
        if(user == null) {
            throw new UnknownAccountException();//没找到帐号
        }
//        if(Boolean.TRUE.equals(user.getLocked())) {
//            throw new LockedAccountException(); //帐号锁定
//        }
        //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user.getUserName(), //用户名
                user.getPassword(), //密码
                getName()  //realm name
        );
        return authenticationInfo;
    }

    public void setUsersService(UsersService usersService) {
        this.usersService = usersService;
    }
}

3、对于MyRealm中需要使用UsersService的方法:usersService.findRoles(username)、usersService.findPermissions(username)。参考代码如下:

@Service
public class UsersServiceImpl implements UsersService {

    @Autowired
    private UsersDao usersDao;
    @Autowired
    private RoleDao roleDao;
    @Autowired
    private PermissionDao permissionDao;
    @Autowired
    private UsersRoleDao usersRoleDao;
    @Autowired
    private RolePermissionDao rolePermissionDao;

    //根据用户名查找用户
    public Users findByUserName(String username){
        List<Users> list = usersDao.findByUserName(username);
        if(list.size()<1){
            return null;
        }else{
            return list.get(0);
        }
    }

   //根据用户名查找用户角色
    public Set<String> findRoles(String username){
        Set<String> roleNameSet = new HashSet<>();
        Integer userId = findByUserName(username).getId();
        List<UserRole> usersRoleList = usersRoleDao.findByUserId(userId);
        for(int i = 0; i < usersRoleList.size(); i++){
           Role role = roleDao.findById(usersRoleList.get(i).getRoleId());
            roleNameSet.add(role.getRoleName());
        }
        return roleNameSet;
    }

    //根据用户名查找用户权限
    public Set<String> findPermissions(String username){
        Set<String> permissionNameSet = new HashSet<>();
        Integer userId = findByUserName(username).getId();
        List<Integer> roleIdList = new ArrayList<>();
        List<UserRole> usersRoleList = usersRoleDao.findByUserId(userId);
        for(int i = 0; i < usersRoleList.size(); i++){
            Integer roleId = roleDao.findById(usersRoleList.get(i).getRoleId()).getId();
            roleIdList.add(roleId);
        }
        List<RolePermission> rolePermissionList = rolePermissionDao.findByRoleIdIn(roleIdList);
        for(int i = 0;i<rolePermissionList.size();i++){
            Permission permission = permissionDao.findById(rolePermissionList.get(i).getPermissionId());
            permissionNameSet.add(permission.getPermissionName());
        }
        return permissionNameSet;
    }

    //省略set方法
}

RolePermissionMapper.xml参考代码如下:

<mapper namespace="com.wzf.dao.RolePermissionDao">
    <resultMap id="RolePermission" type="com.wzf.pojo.RolePermission">
    </resultMap>

    <select id="findByRoleIdIn" resultMap="RolePermission">
      select * from RolePermission where roleId IN
      <foreach collection="list" item="roleId" open="(" close=")" separator=",">
          #{roleId}
      </foreach>
    </select>
</mapper>

4、web.xml中添加shiro监听和过滤器,具体代码如下:

    <!-- Spring和mybatis的配置文件 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:config/spring-mybatis.xml</param-value>
    </context-param>
    
    <!-- 编码过滤器 -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
    
    <!-- Spring监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 防止Spring内存溢出监听器 -->
    <listener>
        <listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
    </listener>
    
    <!-- Spring MVC servlet -->
    <servlet>
        <servlet-name>SpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:config/spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>SpringMVC</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!--Shiro监听和过滤器-->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
        <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>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>ERROR</dispatcher>
    </filter-mapping>

    <welcome-file-list>
        <welcome-file>/WEB-INF/index.jsp</welcome-file>
    </welcome-file-list>

shiro具体使用:
1、登录方法代码如下:

    //登录
    @PostMapping(value = "/login")
    public ModelAndView login(String userName, String password){
        ModelAndView mav = new ModelAndView();
        //密码加密
        String newPassword = PasswordUtil.encodePwd(password);
        UsernamePasswordToken token = new UsernamePasswordToken(userName,newPassword);
        Subject subject = SecurityUtils.getSubject();
        try{
            subject.login(token);
            //mav.addObject("currentUser",userName);
            mav.setViewName("main");
            return mav;
        }catch (Exception e){
            e.printStackTrace();
            mav.setViewName("index");
            mav.addObject("error","用户名或密码错误!");
            return mav;
        }
    }

2、main.jsp代码如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme()+"://"+request.getServerName() +":"+request.getServerPort()+path+"/";
%>
<%@page isELIgnored="false" %>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<html>
<head>
    <title>main.jsp</title>
</head>
<body>
<h1>欢迎您,<shiro:principal/></h1></br>

<shiro:hasRole name="admin">
    <h1>只有admin角色能够看到的内容</h1><br>
</shiro:hasRole>

<shiro:hasRole name="role1">
    <h1>只有role1角色能够看到的内容</h1><br>
</shiro:hasRole>

<shiro:hasRole name="role2">
    <h1>只有role2角色能够看到的内容</h1><br>
</shiro:hasRole>

<shiro:hasPermission name="/student/add">
    <h1>增加用户</h1></br>
</shiro:hasPermission>

<shiro:hasPermission name="/student/delete">
    <h1>删除用户</h1></br>
</shiro:hasPermission>

<shiro:hasPermission name="/student/update">
    <h1>修改用户</h1></br>
</shiro:hasPermission>

<shiro:hasPermission name="/student/select">
    <h1>查询用户</h1></br>
</shiro:hasPermission>

<a href="<%=basePath%>student/add">增加学生</a><br>
<a href="<%=basePath%>student/delete">删除学生</a><br>
<a href="<%=basePath%>student/update">修改学生</a><br>
<a href="<%=basePath%>student/select">查询学生</a><br>
</body>
</html>

3、结果展示


image.png

image.png

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,463评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,868评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,213评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,666评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,759评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,725评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,716评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,484评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,928评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,233评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,393评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,073评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,718评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,308评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,538评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,338评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,260评论 2 352

推荐阅读更多精彩内容