SSM整合shiro实现登陆验证以及权限检测

SSM整合shiro实现登陆验证以及权限检测

Scroll Down

学完一遍shiro框架对于它的登陆验证以及权限检测还是不太熟悉,所以就想到了来个整合。原本以为花不了多长时间.......不过现实很残酷,所以来记录一下整个过程。

创建数据表

下面将创建用户表、角色表、权限表、角色权限表以及用户角色表,分别存储用户信息,角色信息,权限信息以及它们的关联信息,下面是表的定义以及存储的数据。

数据表定义

  1. 用户表
CREATE TABLE `shiro_user` (
  `name` text,
  `passwd` text,
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
  1. 角色表
CREATE TABLE `shiro_role` (
  `role_id` int(11) NOT NULL AUTO_INCREMENT,
  `role_name` text NOT NULL,
  PRIMARY KEY (`role_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

  1. 权限表
CREATE TABLE `shiro_permission` (
  `per_id` int(11) NOT NULL AUTO_INCREMENT,
  `per_name` text NOT NULL,
  PRIMARY KEY (`per_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

4.角色权限表

CREATE TABLE `shiro_role_per` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) NOT NULL,
  `per_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

5.用户角色表

CREATE TABLE `shiro_user_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

权限及角色

  1. 权限
per_idper_name
1delete
2insert
3update
4query
5*
  1. 角色
role_idrole_name
1admin
2anon
3other

3.用户

name    passwd        id
clay    a1346792580    1
whoami    a1346792580    2
anon    anon    3

namepasswdid
claya12345678901
whoamia12345678902
anonanon3
  1. 角色权限
id   role_id    per_id
1    1    1
2    1    2
3    1    3
4    1    4
5    1    5
6    2    4
7    3    2
8    3    3
9    3    4

idrole_idper_id
111
212
313
414
515
624
732
833
934
  1. 用户角色
id   role_id  user_id
1    1    1
2    3    2
3    2    3
idrole_iduser_id
111
232
323

整合shiro

maven添加依赖

因为在ssm中整合shiro,所以需要添加以下依赖

<dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-ehcache</artifactId>
      <version>1.5.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-spring</artifactId>
      <version>1.5.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-web</artifactId>
      <version>1.5.0</version>
    </dependency>
    <dependency>
      <groupId>org.apache.shiro</groupId>
      <artifactId>shiro-core</artifactId>
      <version>1.5.0</version>
    </dependency>

配置shiro

  1. 配置web.xml
    添加过滤器,为了保证所有请求都能被过滤到,该filter需要放在最前面,而且注意使用“/*”匹配所有请求。还需要注意filter-name需要和ioc容器中的一个bean的id相同。
<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>
  </filter-mapping>

  1. 配置applicationContext.xml
    创建applicationContext.xml写入ssm的配置,在此基础上增加以下配置:
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="myRealm"/>
    </bean>
    <!--自定义的realm-->
    <bean id="myRealm" class="com.clay.j2ee.realm.MyRealm"/>
    <!--注意名字-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/admin"/>
        <property name="unauthorizedUrl" value="/refuse"/>
        <property name="filterChainDefinitions">
            <value>
               /admin=anon        <!--anon表示允许未认证访问,authc需要认证-->
                /admin/login=anon
                 <!--配置的登出路径,使用shiro的logout过滤器实现登出-->
                /admin/logout=logout
                /admin/**=authc
              /role_permission/**=authc
            </value>
        </property>
    </bean>

定义的用户、角色、权限pojo

  1. 用户
public class User {
    private String name;
    private String passwd;
    public User(String name,String passwd)
    {
        this.name=name;
        this.passwd=passwd;
    }
     //省略getter及setter等
}
  1. 角色
public class Role
{
    private String roleName;
    private int role_id;
     //省略getter及setter等
}

  1. 权限
public class Permisssion
{
    private String permissionName;
    private int permission_id;
    //省略getter及setter等
}

编写对应的mapper

  1. UserMapper
@Repository
public interface UserMapper
{
    @Select("SELECT *FROM shiro_user WHERE name=#{name}")
    User getUserByName(String name);

}
  1. RoleMapper
@Repository
public interface RoleMapper
{
    @Select("SELECT r.role_id as role_id,r.role_name as roleName FROM\n" +
            "        shiro_role r,\n" +
            "        shiro_user u,\n" +
            "        shiro_user_role sur\n" +
            "        WHERE\n" +
            "        u.name=#{userName}\n" +
            "        AND\n" +
            "        u.id=sur.user_id\n" +
            "        AND\n" +
            "        sur.role_id=r.role_id")
    List<Role> getAllRollesByUserName(String userName);
}
  1. PermissiosMapper
@Repository
public interface PermissionMapper
{
    @Select("SELECT p.per_id as permission_id,p.per_name as permissionName FROM\n" +
            "        shiro_permission p,\n" +
            "        shiro_user_role sur,\n" +
            "        shiro_role_per srp,\n" +
            "        shiro_user u\n" +
            "        WHERE\n" +
            "        u.name=#{userName}\n" +
            "        AND\n" +
            "        sur.user_id=u.id\n" +
            "        AND\n" +
            "        sur.role_id=srp.role_id\n" +
            "        AND\n" +
            "        srp.per_id=p.per_id")
    List<Permisssion>getAllPermissionByUserName(String userName);
}

service层实现

@Service
public class UserServiceImp implements UserService
{
    private UserMapper userMapper;
    private RoleMapper roleMapper;
    private PermissionMapper permissionMapper;
    public UserServiceImp(UserMapper userMapper,
                          RoleMapper roleMapper,
                          PermissionMapper permissionMapper)
    {
        this.userMapper=userMapper;
        this.roleMapper=roleMapper;
        this.permissionMapper=permissionMapper;
    }
    @Override
    public List<Role> getRoleByName(String userName)
    {
        return roleMapper.getAllRollesByUserName(userName);
    }
    @Override
    public User getUserByName(String userName)
    {
        return userMapper.getUserByName(userName);
    }
    @Override
    public List<Permisssion> getPermissionByName(String userName)
    {
        return permissionMapper.getAllPermissionByUserName(userName);
    }
}

自定义realm

public class MyRealm extends AuthorizingRealm
{
    @Autowired
    private UserService userService;
    private static Logger log=Logger.getLogger(MyRealm.class);
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection)
    {
        String userName=principalCollection.getPrimaryPrincipal().toString();
        log.info("用户名:"+userName);
        List<Role>roles=userService.getRoleByName(userName);
      List<Permisssion>permisssions=userService.getPermissionByName(userName);
        SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();

        if (roles==null&&permisssions==null)
        {
            log.error("权限和角色获取均失败.....");
        }
        if (roles!=null)
        {
            Set<String>roleSet=new HashSet<String>();
            for (Role role:roles)
            {
                roleSet.add(role.getRoleName());
            }
            simpleAuthorizationInfo.setRoles(roleSet);
        }
        if (permisssions!=null)
        {
            for (Permisssion permisssion:permisssions)
            {
                simpleAuthorizationInfo.addStringPermission(permisssion.getPermissionName());
            }
        }
        return simpleAuthorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException
    {
        String name=authenticationToken.getPrincipal().toString();
        log.warn("user name:"+name);
        User user=userService.getUserByName(name);
        if (user==null)
        {
            log.warn("用户获取失败。。。。。。");
            return null;
        }
        SimpleAuthenticationInfo simpleAuthenticationInfo=new SimpleAuthenticationInfo(user.getName(),user.getPasswd(),"MyRealm");
        log.info("返回认证信息......");
        return simpleAuthenticationInfo;
    }
}

controller层定义

  1. 登陆验证的控制器关键部分
@org.springframework.stereotype.Controller
@RequestMapping(value = "/admin")
public class Controller
{
    @RequestMapping(value = "/login")
    public String login(User user)
    {
        if (user==null)
        {
            return "redirect:/admin";
        }
        Subject currentUser= SecurityUtils.getSubject();
        UsernamePasswordToken token=new UsernamePasswordToken(user.getName(),user.getPasswd());
       try
       {
           currentUser.login(token);
           currentUser.getSession().setAttribute("user",user);
       }
       catch (AuthenticationException e)
       {
           e.printStackTrace();
           log.info("登陆失败....");
           return "redirect:/admin";
       }
        return "redirect:/admin/index";
    }
    @RequestMapping(value = "/index")
    public String index(Model model, HttpSession session)
    {
        model.addAttribute("user",session.getAttribute("user"));
        return "index";
    }
}
  1. 权限及角色检测控制器
@Controller
@RequestMapping(value = "/role_permission")
public class RolePerController
{
    private static Logger log=Logger.getLogger(RolePerController.class);
    @GetMapping(value = "/admin")
    @ResponseBody
    public boolean admin()
    {
        if (SecurityUtils.getSubject().hasRole("admin"))
        {
            log.info("have admin");
            return true;
        }
        log.info("have no admin");
        return false;
    }
    @GetMapping(value = "/other")
    @ResponseBody
    public boolean other()
    {
        if (SecurityUtils.getSubject().hasRole("other"))
        {
            log.info("has other");
            return true;
        }
        log.info("has on other");
        return false;
    }
    @GetMapping(value = "insert")
    @ResponseBody
    public boolean insert()
    {
        if (SecurityUtils.getSubject().isPermitted("insert"))
        {
            return true;
        }
        return false;
    }
    @GetMapping(value = "/delete")
    @ResponseBody
    public boolean delete()
    {
        if (SecurityUtils.getSubject().isPermitted("delete"))
        {
            return true;
        }
        return false;
    }
    @GetMapping(value = "/update")
    @ResponseBody
    public boolean update()
    {
        if (SecurityUtils.getSubject().isPermitted("update"))
        {
            return true;
        }
        return false;
    }
    @GetMapping(value = "/query")
    @ResponseBody
    public boolean query()
    {
        if (SecurityUtils.getSubject().isPermitted("query"))
        {
            return true;
        }
        return false;
    }

前端

  1. login.jsp
<div class="container">
    <h1 align="center">Login Page</h1><br>
    <form action="${pageContext.request.contextPath}/admin/login" method="post">
        <div class="form-group">
            <label for="name">UserName:</label>
            <input type="text" class="form-control" id="name" name="name">
        </div>
        <div class="form-group">
            <label for="passwd">Passwd:</label>
            <input type="password" class="form-control" id="passwd" name="passwd">
        </div>
        <button type="submit" class="btn btn-primary">submit</button>
    </form>
</div>
  1. index.jsp
//js函数
function testAll(el)
        {
            let url="${pageContext.request.contextPath}/role_permission/";
            $.get(url+el.innerText,function (data)
            {
                if (data===true)
                {
                    alert("You have "+el.innerText);
                }
                else
                {
                    alert("You have no "+el.innerText);
                }
            });
        }
<div class="container">
    <button id="admin" class="btn btn-outline-primary" onclick="testAll(this)">admin</button><br>
    <button class="btn btn-secondary" onclick="testAll(this)">other</button><br><hr>
    <button class="btn btn-warning" onclick="testAll(this)">insert</button>
    <button class="btn btn-danger" onclick="testAll(this)">delete</button>
    <button class="btn btn-info" onclick="testAll(this)">query</button>
    <button class="btn btn-success" onclick="testAll(this)">update</button>
    <br>
    <a href="${pageContext.request.contextPath}/admin/logout" class="btn">logout</a>
</div>

测试

最终采用数据表中的用户clay,进行测试,拥有admin的角色和所有权限,结果如下:

  1. 点击按钮admin验证角色,有
    捕获.PNG

  2. 点击other验证,无
    捕获.PNG

  3. 点击insert等权限,均有
    捕获.PNG

  4. 点击logout,直接被退出。

总结及问题

总结

  1. 注意web.xml和IOC容器中的对应的bean的id相同。
  2. 在整合的时候注意某些jar的版本,相关的jar的版本不同,可能导致报错。
  3. 在配置web.xml时,需要注意web.xml的根节点可能报错,需要添加xml的申明。

问题

  1. 在整合junit进行单元测试的时候,始终找不到XML式的mapper文件。
  2. 对于shiro的过滤链还需要熟悉,还有shiro中的加密配置需要添加。
    推荐:一个shiro资源
  • 写的不好的地方,请看到的大佬指正,谢谢,over!