shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
targetBeanName
shiroFilter
shiroFilter
/*
/logout.action = logout
/refuse.jsp = anon
/validatecode.jsp = anon
/item/queryItem.action = perms[item:query]
/item/editItem.action = perms[item:edit]
/js/** = anon
/images/** = anon
/styles/** = anon
/** = authc
securityManager:这个属性是必须的
package liuxun.ssm.shiro;
import java.util.ArrayList;
import java.util.List;
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.authc.UsernamePasswordToken;
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 liuxun.ssm.po.ActiveUser;
import liuxun.ssm.po.SysPermission;
public class CustomRealm extends AuthorizingRealm {
// 设置Realm名称
@Override
public void setName(String name) {
super.setName("CustomRealm");
}
// 支持UsernamePasswordToken
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
// 用于认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 从token中获取用户身份信息
String username = (String) token.getPrincipal();
// 拿着username从数据库中进行查询
// ....
// 如果查询不到返回null
if (!username.equals("zhangsan")) {
return null;
}
// 获取从数据库查询出来的用户密码
String password = "123"; // 这里使用静态数据进行测试
// 根据用户id从数据库中取出菜单
// ...先使用静态数据
List menus = new ArrayList();
SysPermission sysPermission_1 = new SysPermission();
sysPermission_1.setName("商品管理");
sysPermission_1.setUrl("/item/queryItem.action");
SysPermission sysPermission_2 = new SysPermission();
sysPermission_2.setName("用户管理");
sysPermission_2.setUrl("/user/query.action");
menus.add(sysPermission_1);
menus.add(sysPermission_2);
// 构建用户身份信息
ActiveUser activeUser = new ActiveUser();
activeUser.setUserid(username);
activeUser.setUsername(username);
activeUser.setUsercode(username);
activeUser.setMenus(menus);
// 返回认证信息由父类AuthenticationRealm进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(activeUser, password,
this.getName());
return simpleAuthenticationInfo;
}
// 用于授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取身份信息
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
//用户id
String userid = activeUser.getUserid();
// 根据用户id从数据库中查询权限数据
// ...这里使用静态数据模拟
List permissions = new ArrayList();
permissions.add("item:query");
permissions.add("item:update");
//将权限信息封装为AuthorizationInfo
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//基于资源权限的访问控制
for (String permission : permissions) {
simpleAuthorizationInfo.addStringPermission(permission);
}
// 如果基于角色进行访问控制
// for (String role : roles) {
// simpleAuthorizationInfo.addRole(role);
// }
return simpleAuthorizationInfo;
}
}
//用户登录提交方法
@RequestMapping("/login")
public String login(HttpServletRequest request) throws Exception{
//shiro在认证通过后出现错误后将异常类路径通过request返回
//如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
String exceptionClassName = (String)request.getAttribute("shiroLoginFailure");
if (exceptionClassName!=null) {
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
throw new CustomException("账号不存在");
} else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)){
throw new CustomException("用户名/密码错误");
}else {
throw new Exception(); //最终在设置的异常处理器中生成未知错误
}
}
//此方法不处理登录成功(认证成功)的情况
//如果登录失败还到login页面
return "login";
}
//系统首页
@RequestMapping("/first")
public String first(Model model)throws Exception{
//主体
Subject subject = SecurityUtils.getSubject();
//身份
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
model.addAttribute("activeUser", activeUser);
return "/first";
}
当用户无操作权限,shiro将跳转到refuse.jsp页面。
public class CustomRealm extends AuthorizingRealm {
@Autowired
private SysService sysService;
// 设置Realm名称
@Override
public void setName(String name) {
super.setName("CustomRealm");
}
// 支持UsernamePasswordToken
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
// 用于认证(从数据库中查询用户信息)
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 从token中获取用户身份信息
String userCode = (String) token.getPrincipal();
SysUser sysUser = null;
try {
sysUser = sysService.findSysUserByUserCode(userCode);
} catch (Exception e) {
e.printStackTrace();
}
//如果账号不存在则返回null
if (sysUser == null) {
return null;
}
//根据用户id取出菜单
List menus = null;
try {
menus = sysService.findMenuListByUserId(sysUser.getId());
} catch (Exception e) {
e.printStackTrace();
}
//用户密码
String password = sysUser.getPassword();
//盐
String salt = sysUser.getSalt();
//构建用户身份信息
ActiveUser activeUser = new ActiveUser();
activeUser.setUserid(sysUser.getId());
activeUser.setUsername(sysUser.getUsername());
activeUser.setUsercode(sysUser.getUsercode());
activeUser.setMenus(menus);
//将activeUser设置simpleAuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(activeUser, password,
ByteSource.Util.bytes(salt), this.getName());
return simpleAuthenticationInfo;
}
......
}
// 用于授权(从数据库中查询授权信息)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取身份信息
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
//用户id
String userid = activeUser.getUserid();
//获取用户权限
List permissionsList = null;
try {
permissionsList = sysService.findPermissionListByUserId(userid);
} catch (Exception e) {
e.printStackTrace();
}
//构建shiro授权信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//单独定义一个集合
List permissions = new ArrayList();
for (SysPermission sysPermission : permissionsList) {
//将数据库中的权限标签放入集合
permissions.add(sysPermission.getPercode());
}
simpleAuthorizationInfo.addStringPermissions(permissions);
return simpleAuthorizationInfo;
}
// 查询商品列表信息
@RequestMapping("/queryItem")
@RequiresPermissions("item:query")
public ModelAndView queryItems(HttpServletRequest request) throws Exception {
上边代码中@RequiresPermissions("item:query")表示必须具有"item:query"权限方可执行。
// 方法返回字符串,字符串就是逻辑视图名,Model作用就是将数据填充到request域,在页面展示
@RequestMapping(value="/editItem",method={RequestMethod.GET})
@RequiresPermissions("item:update")
public String editItems(Model model,Integer id) throws Exception{
商品修改提交
// 商品修改提交
// itemsQueryVo是包装类型的pojo
@RequestMapping("/editItemSubmit")
@RequiresPermissions("item:update")
//注意:每个校验pojo的前边必须加@Validated, 每个校验的pojo后边必须加BindingResult接收错误信息
public String editItemSubmit(Model model,Integer id,
@Validated(value={ValidGroup1.class}) @ModelAttribute(value="item")ItemsCustom itemsCustom,
BindingResult bindingResult,
// 上传图片
MultipartFile pictureFile
) throws Exception {
修改
//清空缓存
public void clearCached(){
//清空所有用户的身份缓存信息
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}
在权限修改后调用realm中的方法,realm已经由spring管理,所以从spring中获取realm实例,调用clearCached方法。
@Controller
public class ClearShiroCache {
//注入realm
@Autowired
private CustomRealm customRealm;
@RequestMapping("/clearShiroCache")
public String clearShiroCache(){
//清除缓存,如果按照标准写法是在Service中调用customRealm.clearCached();
customRealm.clearCached();
return "success";
}
}
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
//原FormAuthenticationFilter的认证方法
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//在这里进行验证码的校验
//从Session中获取正确的验证码
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpSession session = httpServletRequest.getSession();
//取出Session中的验证码(正确的验证码)
String validateCode = (String) session.getAttribute("validateCode");
//取出页面的验证码
//输入的验证和session中的验证进行对比
String randomcode = httpServletRequest.getParameter("randomcode");
if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
//如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中
httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
//拒绝访问,不再校验账号和密码
return true;
}
return super.onAccessDenied(request, response);
}
}
......
(2)formAuthenticationFilter定义
自动登陆
springmvc_mybatis_1
contextConfigLocation
classpath:spring/applicationContext-*.xml
org.springframework.web.context.ContextLoaderListener
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring/springmvc.xml
springmvc
*.action
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
targetBeanName
shiroFilter
shiroFilter
/*
CharacterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
utf-8
CharacterEncodingFilter
/*
index.html
index.htm
index.jsp
default.html
default.htm
default.jsp
applicationContext-shiro.xml
/logout.action = logout
/clearShiroCache.action = anon
/refuse.jsp = anon
/validatecode.jsp = anon
/index.jsp = user
/first.action = user
/welcome.jsp = user
/item/** = user
/js/** = anon
/images/** = anon
/styles/** = anon
/** = authc
springmvc.xml中设计shiro注解的配置如下:
shiro-ehcache.xml
CustomRealm.java
package liuxun.ssm.shiro;
import java.util.ArrayList;
import java.util.List;
import org.apache.shiro.SecurityUtils;
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.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cache.CacheManager;
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 liuxun.ssm.po.ActiveUser;
import liuxun.ssm.po.SysPermission;
import liuxun.ssm.po.SysUser;
import liuxun.ssm.service.SysService;
public class CustomRealm extends AuthorizingRealm {
@Autowired
private SysService sysService;
// 设置Realm名称
@Override
public void setName(String name) {
super.setName("CustomRealm");
}
// 支持UsernamePasswordToken
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
// 用于认证(使用静态数据模拟测试)
/**@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 从token中获取用户身份信息
String username = (String) token.getPrincipal();
// 拿着username从数据库中进行查询
// ....
// 如果查询不到返回null
if (!username.equals("zhangsan")) {
return null;
}
// 获取从数据库查询出来的用户密码
String password = "123"; // 这里使用静态数据进行测试
// 根据用户id从数据库中取出菜单
// ...先使用静态数据
List menus = new ArrayList();
SysPermission sysPermission_1 = new SysPermission();
sysPermission_1.setName("商品管理");
sysPermission_1.setUrl("/item/queryItem.action");
SysPermission sysPermission_2 = new SysPermission();
sysPermission_2.setName("用户管理");
sysPermission_2.setUrl("/user/query.action");
menus.add(sysPermission_1);
menus.add(sysPermission_2);
// 构建用户身份信息
ActiveUser activeUser = new ActiveUser();
activeUser.setUserid(username);
activeUser.setUsername(username);
activeUser.setUsercode(username);
activeUser.setMenus(menus);
// 返回认证信息由父类AuthenticationRealm进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(activeUser, password,
this.getName());
return simpleAuthenticationInfo;
}
// 用于授权(使用静态数据进行测试)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取身份信息
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
//用户id
String userid = activeUser.getUserid();
// 根据用户id从数据库中查询权限数据
// ...这里使用静态数据模拟
List permissions = new ArrayList();
permissions.add("item:query");
permissions.add("item:update");
//将权限信息封装为AuthorizationInfo
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//基于资源权限的访问控制
for (String permission : permissions) {
simpleAuthorizationInfo.addStringPermission(permission);
}
// 如果基于角色进行访问控制
// for (String role : roles) {
// simpleAuthorizationInfo.addRole(role);
// }
return simpleAuthorizationInfo;
}
**/
// 用于认证(从数据库中查询用户信息)
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 从token中获取用户身份信息
String userCode = (String) token.getPrincipal();
SysUser sysUser = null;
try {
sysUser = sysService.findSysUserByUserCode(userCode);
} catch (Exception e) {
e.printStackTrace();
}
//如果账号不存在则返回null
if (sysUser == null) {
return null;
}
//根据用户id取出菜单
List menus = null;
try {
menus = sysService.findMenuListByUserId(sysUser.getId());
} catch (Exception e) {
e.printStackTrace();
}
//用户密码
String password = sysUser.getPassword();
//盐
String salt = sysUser.getSalt();
//构建用户身份信息
ActiveUser activeUser = new ActiveUser();
activeUser.setUserid(sysUser.getId());
activeUser.setUsername(sysUser.getUsername());
activeUser.setUsercode(sysUser.getUsercode());
activeUser.setMenus(menus);
//将activeUser设置simpleAuthenticationInfo
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(activeUser, password,
ByteSource.Util.bytes(salt), this.getName());
return simpleAuthenticationInfo;
}
// 用于授权(从数据库中查询授权信息)
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取身份信息
ActiveUser activeUser = (ActiveUser) principals.getPrimaryPrincipal();
//用户id
String userid = activeUser.getUserid();
//获取用户权限
List permissionsList = null;
try {
permissionsList = sysService.findPermissionListByUserId(userid);
} catch (Exception e) {
e.printStackTrace();
}
//构建shiro授权信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//单独定义一个集合
List permissions = new ArrayList();
for (SysPermission sysPermission : permissionsList) {
//将数据库中的权限标签放入集合
permissions.add(sysPermission.getPercode());
}
simpleAuthorizationInfo.addStringPermissions(permissions);
return simpleAuthorizationInfo;
}
//清除用户的授权信息
//清空缓
public void clearCached(){
//清空所有用户的身份缓存信息
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}
}
CustomFormAuthenticationFilter.java
package liuxun.ssm.shiro;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
/**
* 认证之前实现验证码校验
* @author liuxun
*
*/
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
//原FormAuthenticationFilter的认证方法
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//在这里进行验证码的校验
//从Session中获取正确的验证码
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpSession session = httpServletRequest.getSession();
//取出Session中的验证码(正确的验证码)
String validateCode = (String) session.getAttribute("validateCode");
//取出页面的验证码
//输入的验证和session中的验证进行对比
String randomcode = httpServletRequest.getParameter("randomcode");
if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
//如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中
httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
//拒绝访问,不再校验账号和密码
return true;
}
return super.onAccessDenied(request, response);
}
}
LoginController.java
package liuxun.ssm.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import liuxun.ssm.exception.CustomException;
import liuxun.ssm.po.ActiveUser;
import liuxun.ssm.service.SysService;
/**
* 登录和退出
* @author liuxun
*
*/
@Controller
public class LoginController {
@Autowired
private SysService sysService;
//用户登录提交方法
/*@RequestMapping("/login")
public String login(HttpSession session,String randomcode,String usercode,String password) throws Exception{
// 校验验证码,防止恶性攻击
// 从Session中获取正确的验证码
String validateCode = (String) session.getAttribute("validateCode");
//输入的验证码和Session中的验证码进行对比
if (!randomcode.equalsIgnoreCase(validateCode)) {
//抛出异常
throw new CustomException("验证码输入错误");
}
//调用Service校验用户账号和密码的正确性
ActiveUser activeUser = sysService.authenticat(usercode, password);
//如果Service校验通过,将用户身份记录到Session
session.setAttribute("activeUser", activeUser);
//重定向到商品查询页面
return "redirect:/first.action";
} */
//用户登录提交方法
@RequestMapping("/login")
public String login(HttpServletRequest request) throws Exception{
//shiro在认证通过后出现错误后将异常类路径通过request返回
//如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
String exceptionClassName = (String)request.getAttribute("shiroLoginFailure");
if (exceptionClassName!=null) {
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
throw new CustomException("账号不存在");
} else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)){
throw new CustomException("用户名/密码错误");
}else if("randomCodeError".equals(exceptionClassName)){
throw new CustomException("验证码错误");
} else{
throw new Exception(); //最终在设置的异常处理器中生成未知错误
}
}
//此方法不处理登录成功(认证成功)的情况
//如果登录失败还到login页面
return "login";
}
//用户退出
/*
@RequestMapping("/logout")
public String logout(HttpSession session) throws Exception{
//session失效
session.invalidate();
//重定向到商品查询页面
return "redirect:/first.action";
}
*/
}
FirstAction.java
package liuxun.ssm.controller;
import java.util.List;
import javax.servlet.http.HttpSession;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import liuxun.ssm.po.ActiveUser;
@Controller
public class FirstAction {
//系统首页
@RequestMapping("/first")
public String first(Model model)throws Exception{
//主体
Subject subject = SecurityUtils.getSubject();
//身份
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
model.addAttribute("activeUser", activeUser);
return "/first";
}
//欢迎页面
@RequestMapping("/welcome")
public String welcome(Model model)throws Exception{
return "/welcome";
}
}