一、项目的版本
Spring 全家桶-4.3.5版本
shiro版本-1.3.2 核心容器
下面是maven依赖的配置
UTF-8
1.7
1.7
4.3.5.RELEASE
1.4.4.RELEASE
3.2.8
4.3.5.Final
5.1.29
commons-logging
commons-logging
1.2
org.apache.shiro
shiro-core
1.3.2
org.apache.shiro
shiro-web
1.3.2
org.apache.shiro
shiro-spring
1.2.2
org.apache.shiro
shiro-ehcache
1.3.2
org.springframework
spring-core
${spring.version}
org.springframework
spring-web
${spring.version}
org.springframework
spring-oxm
${spring.version}
org.springframework
spring-tx
${spring.version}
org.springframework
spring-jdbc
${spring.version}
org.springframework
spring-webmvc
${spring.version}
org.springframework
spring-context
${spring.version}
org.springframework
spring-context-support
${spring.version}
org.springframework
spring-aop
${spring.version}
二、配置shiro文件
Shiro Configuration
/resource/** = anon
/images/** = anon
/user/VerifyImg = anon
/user/login = anon
/Forum/getForum =anon
/city/bbs =anon
/city/reg =anon
/Register= anon
/getPhoto =anon
/** = authc
三、编写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他底层有一个通用参数类型,我这个是通用参数的类型。