学完一遍shiro框架对于它的登陆验证以及权限检测还是不太熟悉,所以就想到了来个整合。原本以为花不了多长时间.......不过现实很残酷,所以来记录一下整个过程。
创建数据表
下面将创建用户表、角色表、权限表、角色权限表以及用户角色表,分别存储用户信息,角色信息,权限信息以及它们的关联信息,下面是表的定义以及存储的数据。
数据表定义
- 用户表
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;
- 角色表
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;
- 权限表
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;
权限及角色
- 权限
per_id | per_name |
---|---|
1 | delete |
2 | insert |
3 | update |
4 | query |
5 | * |
- 角色
role_id | role_name |
---|---|
1 | admin |
2 | anon |
3 | other |
3.用户
name passwd id
clay a1346792580 1
whoami a1346792580 2
anon anon 3
name | passwd | id |
---|---|---|
clay | a1234567890 | 1 |
whoami | a1234567890 | 2 |
anon | anon | 3 |
- 角色权限
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
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 |
- 用户角色
id role_id user_id
1 1 1
2 3 2
3 2 3
id | role_id | user_id |
---|---|---|
1 | 1 | 1 |
2 | 3 | 2 |
3 | 2 | 3 |
整合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
- 配置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>
- 配置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
- 用户
public class User {
private String name;
private String passwd;
public User(String name,String passwd)
{
this.name=name;
this.passwd=passwd;
}
//省略getter及setter等
}
- 角色
public class Role
{
private String roleName;
private int role_id;
//省略getter及setter等
}
- 权限
public class Permisssion
{
private String permissionName;
private int permission_id;
//省略getter及setter等
}
编写对应的mapper
- UserMapper
@Repository
public interface UserMapper
{
@Select("SELECT *FROM shiro_user WHERE name=#{name}")
User getUserByName(String name);
}
- 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);
}
- 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层定义
- 登陆验证的控制器关键部分
@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";
}
}
- 权限及角色检测控制器
@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;
}
前端
- 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>
- 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的角色和所有权限,结果如下:
-
点击按钮admin验证角色,有
-
点击other验证,无
-
点击insert等权限,均有
-
点击logout,直接被退出。
总结及问题
总结
- 注意web.xml和IOC容器中的对应的bean的id相同。
- 在整合的时候注意某些jar的版本,相关的jar的版本不同,可能导致报错。
- 在配置web.xml时,需要注意web.xml的根节点可能报错,需要添加xml的申明。
问题
- 在整合junit进行单元测试的时候,始终找不到XML式的mapper文件。
- 对于shiro的过滤链还需要熟悉,还有shiro中的加密配置需要添加。
推荐:一个shiro资源
- 写的不好的地方,请看到的大佬指正,谢谢,over!