目录
一、shiro 授权角色、权限
1.授权
授角色:用户具备哪些角色
授权限:用户具备哪些权限
看下图可了解↓
这五张表就分别对应了我数据库里的五张表的数据
我们的zdm它的角色有管理员和普通用户,它是通过链表查询得到的数据
授角色:用户具备哪些角色
select roleid from t_shiro_user u,t_shiro_user_role ur where u.userid = ur.userid and username='zdm'
授权限:用户具备哪些权限
select rp.perid from t_shiro_user u,t_shiro_user_role ur,t_shiro_role_permission rp where u.userid = ur.userid and ur.roleid = rp.roleid and u.username='ww'
步骤:
1.Mapper层、service层
2.shiro的授权方法
注意:角色与权限的结果要与spring-shiro.xml中的配置保持一致
角色:1,4 权限:1,2,3,4,5
3.测试
UserMapper.xml 中新增几个内容的方法
<select id="selectRoleIdsByUserName" resultType="java.lang.String" parameterType="java.lang.String" >
select roleid from t_shiro_user u,t_shiro_user_role ur
where u.userid = ur.userid and username=#{userName}
</select>
<select id="selectPerIdsByUserName" resultType="java.lang.String" parameterType="java.lang.String" >
select rp.perid from t_shiro_user u,t_shiro_user_role ur,t_shiro_role_permission rp
where u.userid = ur.userid and ur.roleid = rp.roleid and u.username=#{userName}
</select>
UserMapper.java
package com.jwj.ssm.mapper;
import com.jwj.ssm.model.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.Set;
//这个加也可以不加也可以,不加那边的实现方法就会报红,会感觉不舒服,加了就不会报红
@Repository
public interface UserMapper {
int deleteByPrimaryKey(Integer userid);
int insert(User record);
int insertSelective(User record);
User selectByPrimaryKey(Integer userid);
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);
// 通过 账户名 查询用户信息
User queryUserByUserName(@Param("userName") String userName);
// 通过 账户名 查询 对应的 权限
// 这里返回Set Set 和 List 的区别的是:Set 不可以重复,List 是可以重复的
// 所有说我们要将权限剔除
Set<String> selectRoleIdsByUserName(@Param("userName") String userName);
// 通过 用户名 查询 对应的 角色
Set<String> selectPerIdsByUserName(@Param("userName") String userName);
}
UserBiz.java
package com.jwj.ssm.biz;
import com.jwj.ssm.model.User;
import org.apache.ibatis.annotations.Param;
import java.util.Set;
/**
* @author 敢敢
* @site www.javajwj.com
* @company xxx公司
* @create 2022-08-25 19:10
*/
public interface UserBiz {
int deleteByPrimaryKey(Integer userid);
int insert(User record);
int insertSelective(User record);
User selectByPrimaryKey(Integer userid);
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);
User queryUserByUserName(String userName);
Set<String> selectRoleIdsByUserName(String userName);
Set<String> selectPerIdsByUserName(String userName);
}
UserBizImpl.java
package com.jwj.ssm.biz.impl;
import com.jwj.ssm.biz.UserBiz;
import com.jwj.ssm.mapper.UserMapper;
import com.jwj.ssm.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;
/**
* @author 敢敢
* @site www.javajwj.com
* @company xxx公司
* @create 2022-08-25 19:11
*/
@Service("userBiz")
public class UserBizImpl implements UserBiz {
@Autowired
private UserMapper userMapper;
@Override
public int deleteByPrimaryKey(Integer userid) {
return userMapper.deleteByPrimaryKey(userid);
}
@Override
public int insert(User record) {
return userMapper.insert(record);
}
@Override
public int insertSelective(User record) {
return userMapper.insertSelective(record);
}
@Override
public User selectByPrimaryKey(Integer userid) {
return userMapper.selectByPrimaryKey(userid);
}
@Override
public int updateByPrimaryKeySelective(User record) {
return userMapper.updateByPrimaryKeySelective(record);
}
@Override
public int updateByPrimaryKey(User record) {
return userMapper.updateByPrimaryKey(record);
}
@Override
public User queryUserByUserName(String userName) {
return userMapper.queryUserByUserName(userName);
}
@Override
public Set<String> selectRoleIdsByUserName(String userName) {
return userMapper.selectRoleIdsByUserName(userName);
}
@Override
public Set<String> selectPerIdsByUserName(String userName) {
return userMapper.selectPerIdsByUserName(userName);
}
}
重写自定义realm中的授权方法
MyRealm.java
package com.jwj.ssm.shiro;
import com.jwj.ssm.biz.UserBiz;
import com.jwj.ssm.model.User;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Set;
/**
* @author 敢敢
* @site www.javajwj.com
* @company xxx公司
* @create 2022-08-25 19:19
*/
public class MyRealm extends AuthorizingRealm {
public UserBiz userBiz;
public UserBiz getUserBiz() {
return userBiz;
}
public void setUserBiz(UserBiz userBiz) {
this.userBiz = userBiz;
}
/**
* 授权
* @param principalCollection
* @return
* shiro-web.ini
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取到我们的账户名
String userName = principalCollection.getPrimaryPrincipal().toString();
// 查询对应的角色
Set<String> roleIds = userBiz.selectRoleIdsByUserName(userName);
Set<String> perIds = userBiz.selectPerIdsByUserName(userName);
// 授权器
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 将当前登录的 权限 交给 shiro的授权器
info.setStringPermissions(perIds);
// 将当前登录的 角色 交给 shiro的授权器
info.setRoles(roleIds);
return info;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
* shiro.ini
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 拿到我们的用户名
String userName = authenticationToken.getPrincipal().toString();
User user = userBiz.queryUserByUserName(userName);
// 拿到数据库中的用户信息,放入token凭证中,用于controler进行对比
AuthenticationInfo info = new SimpleAuthenticationInfo(
user.getUsername(),
user.getPassword(),
ByteSource.Util.bytes(user.getSalt()),
this.getName() //realm的名字
);
return info;
}
}
applicationContext-shiro.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 第一种方式 <bean class="com.jwj.ssm.biz.impl.UserBizImpl" id="userBiz"></bean>-->
<!--配置自定义的Realm-->
<bean id="shiroRealm" class="com.jwj.ssm.shiro.MyRealm">
<property name="userBiz" ref="userBiz" />
<!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
<!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
<!--注意:重要的事情说三次~~~~~~此处加密方式要与用户注册时的算法一致 -->
<!--以下三个配置告诉shiro将如何对用户传来的明文密码进行加密-->
<property name="credentialsMatcher">
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!--指定hash算法为MD5-->
<property name="hashAlgorithmName" value="md5"/>
<!--指定散列次数为1024次-->
<property name="hashIterations" value="1024"/>
<!--true指定Hash散列值使用Hex加密存. false表明hash散列值用用Base64-encoded存储-->
<property name="storedCredentialsHexEncoded" value="true"/>
</bean>
</property>
</bean>
<!--注册安全管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroRealm" />
</bean>
<!--Shiro核心过滤器-->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager" />
<!-- 身份验证失败,跳转到登录页面 -->
<property name="loginUrl" value="/login"/>
<!-- 身份验证成功,跳转到指定页面 -->
<!--<property name="successUrl" value="/index.jsp"/>-->
<!-- 权限验证失败,跳转到指定页面 -->
<property name="unauthorizedUrl" value="/unauthorized.jsp"/>
<!-- Shiro连接约束配置,即过滤链的定义 -->
<property name="filterChainDefinitions">
<value>
<!--
注:anon,authcBasic,auchc,user是认证过滤器
perms,roles,ssl,rest,port是授权过滤器
-->
<!--anon 表示匿名访问,不需要认证以及授权-->
<!--authc表示需要认证 没有进行身份认证是不能进行访问的-->
<!--roles[admin]表示角色认证,必须是拥有admin角色的用户才行-->
/user/login=anon
/user/updatePwd.jsp=authc
/admin/*.jsp=roles[4]
/user/teacher.jsp=perms[2]
<!-- /css/** = anon
/images/** = anon
/js/** = anon
/ = anon
/user/logout = logout
/user/** = anon
/userInfo/** = authc
/dict/** = authc
/console/** = roles[admin]
/** = anon-->
</value>
</property>
</bean>
<!-- Shiro生命周期,保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
</beans>
我们来测试一下:我们先登录一下 用zs登录一下
我们在换zdm进行登录 密码123
我们在来测试一下teacher.jsp,我们先用zs登录,zs没有这个权限所以它不能访问
当我们用ls来登录的时候,因为ls它就具备有这个权限,所以可以访问
二、shiro注解式开发
① 将对应注解添加到指定需要权限控制的方法上
身份认证:requireUser
角色认证:requireRole
权限认证:requirePermission
② 在SpringMVC.xml中添加拦截器相关的配置
我们用ls登录进去的时候shiro标签下少两个东西
我们交给授权器的是id,所以我们在main.jsp里面就要换
mian.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="r" uri="http://shiro.apache.org/tags" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>主界面<%=System.currentTimeMillis()%>,欢迎您:[${sessionScope.username}]</h1>
<ul>
系统功能列表
<li>
<a href="admin/addUser.jsp">用户新增</a>
</li>
<li>
<a href="admin/listUser.jsp">用户查询</a>
</li>
<li>
<a href="admin/resetPwd.jsp">重置用户密码</a>
</li>
<li>
<a href="admin/updateUser.jsp">用户修改</a>
</li>
<li>
<a href="user/updatePwd.jsp">个人密码修改</a>
</li>
<li>
<a href="user/teacher.jsp">老师简介</a>
</li>
<li>
<a href="${pageContext.request.contextPath}/logout">退出系统</a>
</li>
</ul>
<ul>
shiro标签
<li>
<r:hasPermission name="1">
<a href="admin/addUser.jsp">用户新增</a>
</r:hasPermission>
</li>
<li>
<a href="admin/listUser.jsp">用户查询</a>
</li>
<li>
<a href="admin/resetPwd.jsp">重置用户密码</a>
</li>
<li>
<r:hasPermission name="2">
<a href="admin/updateUser.jsp">用户修改</a>
</r:hasPermission>
</li>
<li>
<a href="user/updatePwd.jsp">个人密码修改</a>
</li>
<li>
<a href="${pageContext.request.contextPath}/logout">退出系统</a>
</li>
</ul>
</body>
</html>
我们在用ls进行登录如图所示:
常用注解介绍
@RequiresAuthenthentication:表示当前Subject已经通过login进行身份验证;即 Subject.isAuthenticated()返回 true
@RequiresUser:表示当前Subject已经身份验证或者通过记住我登录的
@RequiresGuest:表示当前Subject没有身份验证或者通过记住我登录过,即是游客身份
@RequiresRoles(value = {"admin","user"},logical = Logical.AND):表示当前Subject需要角色admin和user
@RequiresPermissions(value = {"user:delete","user:b"},logical = Logical.OR):表示当前Subject需要权限user:delete或者user:b
注解的使用
controller层 中的
ShiroController.java
package com.jwj.ssm.controller;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author 敢敢
* @site www.javajwj.com
* @company xxx公司
* @create 2022-08-26 20:21
*/
@RequestMapping("/shiro")
@Controller
public class ShiroController {
// @RequiresUser代表 当前方法只有登录后才能够访问
// RequiresUser等价于 spring-shiro.xml中的/user/updatePwd.jsp=authc配置
@RequiresUser
@RequestMapping("/passUser")
public String passUser(){
System.out.println("身份认证通过...");
return "admin/addUser";
}
// @RequiresRoles 当前方法只有 具备指定的角色 才能够访问
// RequiresRoles 等价于 spring-shiro.xml中的/admin/*.jsp=roles[4]配置
@RequiresRoles(value = {"1","4"},logical = Logical.AND)
@RequestMapping("/passRole")
public String passRole(){
System.out.println("角色认证通过...");
return "admin/addUser";
}
// @RequiresPermissions 当前方法只有 具备指定的权限 才能够访问
// RequiresPermissions 等价于 spring-shiro.xml中的/user/teacher.jsp=perms[2]配置
@RequiresPermissions(value = {"2"},logical = Logical.AND)
@RequestMapping("/passPermission")
public String passPermission(){
System.out.println("权限认证通过...");
return "admin/addUser";
}
}
在 springmvc-servle.xml 中添加拦截器的配置
我们身份认证失败的时候肯定要有地方跳
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true"></property>
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.apache.shiro.authz.UnauthorizedException">
unauthorized
</prop>
</props>
</property>
<property name="defaultErrorView" value="unauthorized"/>
</bean>
我们在运行一下如图所示:
怎么解决:我们用zs进行登录,可以访问了
我们在用passRole查询一下:
我们只要换一个具备这两个条件的权限即可查询到假设我们换admin进行登录就可以查询到
我们把ShiroController.java 这里换成or
我们就用之前的zs进行登录 如图所示:
三、总结
1.授权
关于权限相关的五张表
用户表 角色表 用户角色中间表 权限表 角色权限中间表
1.用户所拥有的角色
我们只需要角色的id所以我们连表查询只需要两张表 角色表 用户角色中间表
2.用户所拥有的权限
我们通过用户表 用户角色中间表 角色权限中间表 就能拿到权限的id了
步骤:
1.Mapper层、service层
2.MyRealm中的授权方法
查询用户所拥有的角色及权限赋值给授权器
3.测试
2.注解式开发
why:每一个页面每一个请求都需要在Spring-shiro.xml中做配置
①常用注解:标记在方法上
身份认证:requireUser
角色认证:requireRole
权限认证:requirePermission
②SpringMVC.xml添加拦截器配置