一、项目的版本
Spring 全家桶-4.3.5版本
shiro版本-1.3.2 核心容器
下面是maven依赖的配置
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<!-- spring版本号 -->
<spring.version>4.3.5.RELEASE</spring.version>
<spring-data-jpa.version>1.4.4.RELEASE</spring-data-jpa.version>
<!-- mybatis版本号 -->
<mybatis.version>3.2.8</mybatis.version>
<!-- hibernate版本号 -->
<hibernateVersion>4.3.5.Final</hibernateVersion>
<!-- mysql驱动版本号 --><!-- 添加mysql驱动依赖 ——在使用5.0+版本数据时使用此驱动-->
<mysql-driver.version>5.1.29</mysql-driver.version>
<!-- mysql驱动版本号 -->
<!--<mysql-driver.version>8.0.12</mysql-driver.version>-->
</properties>
<!-- shiro 核心依赖 -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 添加spring核心依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
二、配置shiro文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"
default-lazy-init="true">
<description>Shiro Configuration</description>
<!-- 加载配置属性文件 -->
<!--<context:property-placeholder ignore-unresolvable="true" location="classpath:Shiro.properties" />-->
<!-- 支持Shiro对Controller的方法级AOP安全控制 begin-->
<!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
<bean id="myRealm" class="com.BBS.Filter.ShiroSecurity.AuthenticationFilter">
<property name="authorizationCacheName" value="shiroCache"/>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 单一 Realm 应用这样写。如果有多个 Realm ,可以使用 "realms" 属性 -->
<property name="realm" ref="myRealm"/>
<!-- 对 Realm 里面的subject进行缓存-->
<property name="cacheManager" ref="cacheManager"/>
</bean>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!-- 拦截后,发现未登录的跳转到登录页面 -->
<property name="loginUrl" value="/shiroLogin"/>
<!-- 登录成功后的页面 -->
<property name="successUrl" value="bbs_jsp/index"/>
<!-- 非法访问跳转的页面 -->
<property name="unauthorizedUrl" value="/403"/>
<!-- 权限配置 -->
<property name="filterChainDefinitions">
<value>
<!-- 无需认证即可访问的静态资源,还可以添加其他 url -->
/resource/** = anon
/images/** = anon
<!-- 无需认证即可访问的 url -->
/user/VerifyImg = anon
/user/login = anon
/Forum/getForum =anon
<!--/shiroLogin =anon-->
/city/bbs =anon
/city/reg =anon
/Register= anon
/getPhoto =anon
<!-- 除了上述忽略的资源,其他所有资源都需要认证后才能访问 -->
/** = authc
</value>
</property>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<aop:config proxy-target-class="true"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- 支持Shiro对Controller的方法级AOP安全控制 end -->
<!--Shiro 权限实体缓存 -->
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:shiroCache.xml"/>
<!--value="classpath:org/apache/shiro/cache/ehcache/ehcache.xml"-->
<property name="shared" value="true"/>
</bean>
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<!-- 这个属性方法里面有Shiro的自带默认缓存配置,我这里使用的默认配置,可以根据自己的需要进行修改
具体的可以参看里面的XML文件的参数-->
<property name="cacheManager" ref="ehCacheManager">
</property>
</bean>
<!--Shiro 权限实体缓存 -->
</beans>
三、编写shiro权限认证类
这个认证类是跟着上面的配置文件,这个拦截类是可以配置多个,每个应用的地方都可以不一样,我这里配置的主要是拦截用户的总体项目的访问权限模块。大家可以根据自己的需要创建不同的拦截类。
这里面的service类我就不贴出来了,大家可以根据自己的实际service来进行业务操作。当然有需要的可以私下留言
import com.BBS.pojo.User;
import com.BBS.service.GeneralService;
import com.BBS.service.UserService;
import org.apache.shiro.authc.*;
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.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* @author zhanglq
* @version 1.0
* @date 2019/11/15 11:34
* @info shiro权限认证类
*/
public class AuthenticationFilter extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private GeneralService generalService;
/**
* shiro授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 使用Shiro提供的方法获取用户名称
User user = (User) getAvailablePrincipal(principalCollection);
if (user != null) {
List list = generalService.findForJdbc("select roleId from user_role where user_id = ?",new Object[]{user.getId()});
String roleId = (String) list.get(0);
// 获取用户的权限
List roleList = generalService.findForJdbc("select res_id from role_res where role_id = ?", new Object[]{roleId});
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if (roleId != null) {
info.addRole(roleId);
}
if (roleList != null) {
info.addStringPermission(roleList.toString());
}
return info;
}
return null;
}
/**
* shiro认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String loginName = token.getUsername();
if (loginName != null &&! "".equals(loginName)) {
//通过登录名获取用户
User user = userService.findByLoginName(loginName);
if (user != null) {
// 如果身份认证验证成功,返回一个AuthenticationInfo实现
return new SimpleAuthenticationInfo(user.getLoginname(),user.getPassword(),user.getId());
}
}
return null;
}
}
四、接下是controller 层实现权限的认证。
上面那个类,可以使用注解的形式访问,也可以自己主动访问这个认证类,去查询相关角色用户有没有对应的权限操作。
@RequestMapping(value = "/login", method = RequestMethod.POST)
public ModelAndView loginPost(ModelAndView mv, RedirectAttributes redirectAttributes, HttpServletRequest request,User user) {
String verifyCode = request.getSession().getAttribute("verifyCode").toString().toLowerCase();//获取session中的验证码 不区分大小写
String vercode = request.getParameter("vercode").toLowerCase();//获取用户提交的验证码
Subject currentUser = SecurityUtils.getSubject();
//1. 接受提交的当事人的证书,以及host地址,第一参数用户名,第二个参数密码,第三个参数host,我这里传了ID:
UsernamePasswordToken token = new UsernamePasswordToken(user.getLoginname(), user.getPassword(), user.getId());
try {
//用户认证
currentUser.login(token);
} catch (AuthenticationException e) {
System.out.println(e.getMessage());
redirectAttributes.addFlashAttribute("message", "用户名或密码错误!");
mv.setViewName("bbs_jsp/user/Login");
return mv;
}
if (currentUser.isAuthenticated()) {
//登录成功,保存用户相关信息
User session_user = this.userService.findByLoginName(user.getLoginname());
SessionUtils.setAttr(request,AppConstant.SESSION_USER,session_user);
//跳转成功页面
mv.setViewName("bbs_jsp/index");
mv.addObject("Forum", page);
mv.addObject("Weeks", weeks);
return mv;
} else {
redirectAttributes.addFlashAttribute("message", "用户名或密码错误!");
mv.setViewName("bbs_jsp/user/Login");
return mv;
}
}
上面那里我是主动调用的shiro 的主体类,subject去查询用户的相关权限。subject是核心,每一个用户都可以是一个subject。当然也可以用使用注解去验证
Shiro的认证注解处理是有内定的处理顺序的,如果有个多个注解的话,前面的通过了会继续检查后面的,若不通过则直接返回,处理顺序依次为(与实际声明顺序无关):
当前Subject必须拥有所有指定的角色时,才能访问被该注解标注的方法。如果当天Subject不同时拥有所有指定角色,则方法不会执行还会抛出AuthorizationException异常。
RequiresRoles
当前Subject需要拥有某些特定的权限时,才能执行被该注解标注的方法。如果当前Subject不具有这样的权限,则方法不会被执行。
RequiresPermissions
使用该注解标注的类,实例,方法在访问或调用时,当前Subject必须在当前session中已经过认证
RequiresAuthentication
当前Subject必须是应用的用户,才能访问或调用被该注解标注的类,实例,方法。
RequiresUser
使用该注解标注的类,实例,方法在访问或调用时,当前Subject可以是“gust”身份,不需要经过认证或者在原先的session中存在记录。
RequiresGuest
上面是的排序是执行的顺序
使用列子
//必须同时属于user和admin角色,可以填写多个 @RequiresRoles({"user","admin"})
//符合什么index:hello权限
@RequiresPermissions("index:hello")
下面还需要存放shiro权限的表
/**
* @author zhanglq
* @version 1.0
* @date 2019/11/4 21:47
* @idfo 角色资源权限表
*/
@Entity
@Table(name = "shiro_resource")
public class ResourceEntity extends commonEntity {
private String resName; //资源权限名称
@Column(name = "res_name",length = 25)
public String getResName() {
return resName;
}
public void setResName(String resName) {
this.resName = resName;
}
}
/**
* @author zhanglq
* @version 1.0
* @date 2019/11/4 22:07
* @info 用户角色表
*/
@Entity
@Table(name = "role")
public class Role extends commonEntity {
private String roleName; // 角色名称
@Column(name = "role_name",length = 10)
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
}
/**
* @author zhanglq
* @version 1.0
* @date 2019/11/4 21:00
* @info 角色-资源表,记录每个角色所有的资源权限
*/
@Entity
@Table(name = "role_res")
public class RoleResEntity extends commonEntity {
private String roleId; //角色id
private String resId; //资源权限ID
@Column(name = "role_id",length = 5)
public String getRoleId() {
return roleId;
}
public void setRoleId(String roleId) {
this.roleId = roleId;
}
@Column(name = "res_id",length = 5)
public String getResId() {
return resId;
}
public void setResId(String resId) {
this.resId = resId;
}
}
/**
* @author zhanglq
* @version 1.0
* @date 2019/11/18 11:08
* @info 用户角色表,记录每个用户所有的角色信息
*/
@Entity
@Table(name = "user_role")
public class UserRole extends commonEntity {
private String userId; // 用户表ID
private String roleId; //角色表ID
@Column(name = "user_id",length = 5)
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
@Column(name = "role_id",length = 5)
public String getRoleId() {
return roleId;
}
public void setRoleId(String roleId) {
this.roleId = roleId;
}
}
shiro的缓存参数,在shiro配置文件我配置了shiro的缓存,我查看过源码,如果我们配置了缓存类,但是没有配置参数的话。shiro他底层有一个通用参数类型,我这个是通用参数的类型。
<ehcache updateCheck="false" name="shiroCache">
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"/>
</ehcache>