ShiroJAVA

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

2018-04-23  本文已影响282人  WebGiser

本文主要讲解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
上一篇 下一篇

猜你喜欢

热点阅读