1. shiro介绍
shiro的程序运行流程图:
Application code:应用程序代码,开发人员编写的代码
Subject:主体,当前用户
SecurityManager:安全管理器,shiro框架的核心对象,管理各个组件
Realm:类似于Dao,负责访问安全数据(用户数据、角色数据、权限数据)
2.shiro的表单认证流程(可以自定义表单认证)
参考http://blog.csdn.net/zcl_love_wx/article/details/51577058
实际中可能会有点差别,灵活处理
1.已经登录,放行
3.未登录,判断是否是登录请求,不是则跳转到登录页面,并保存当前请求
5.是登录请求,并且为get请求,则直接跳转到loginUrl,即登录页面
6.是post请求,说明从登录页面提交了表单,开始表单,realm验证
7.验证失败,则将错误信息存入shiroLoginFailure中,并重新跳转到登录页面,并显示错误信息
8.验证成功则跳转到之前保存的请求,没有则跳转到successUrl指定页面
注意:登录请求/login被拦截进行验证,最后还是会跳到/login的Controller中
3.实际应用到项目中
- 导入相关的包,shiro-all.jar
- 在web.xml文件中配置Spring用于整合shiro的过滤器
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
true
targetFilterLifecycle
true
shiroFilter
/*
- 在Spring-mvc中配置一个bean,id和之前过滤器的名称相同
/assets/** = anon
/*.ico = anon
/nirvana/** = anon
/login = authc
/logout = logout
/** = authc
具体细节就不再描述了,请百度自行了解
- 根据shiro的介绍,配置安全管理器securityManager,及其依赖的相关属性
这里自定义了表单过滤器和登出过滤器
- 自定义表单过滤器CustomFormAuthenticationFilter
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
// 创建 Token
protected CaptchaUsernamePasswordToken createToken(
ServletRequest request, ServletResponse response) {
String username = getUsername(request);
String password = getPassword(request);
String captcha = getCaptcha(request);
boolean rememberMe = isRememberMe(request);
return new CaptchaUsernamePasswordToken(username, password.toCharArray(), rememberMe, captcha);
}
// 认证
protected boolean executeLogin(ServletRequest request,
ServletResponse response) throws Exception {
CaptchaUsernamePasswordToken token = createToken(request, response);
try {
/* doCaptchaValidate((HttpServletRequest) request, token);*/
Subject subject = getSubject(request, response);
subject.login(token);
return onLoginSuccess(token, subject, request, response);
} catch (AuthenticationException e) {
return onLoginFailure(token, e, request, response);
}
}
}
- 自定义登出过滤器
public class CoustomLogoutFilter extends LogoutFilter {
private static final Logger log = LoggerFactory.getLogger(CoustomLogoutFilter.class);
public CoustomLogoutFilter() {}
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
Subject subject = getSubject(request, response);
String redirectUrl = getRedirectUrl(request, response, subject);
try {
subject.logout();
} catch (SessionException ise) {
log.debug("Encountered session exception during logout. This can generally safely be ignored.", ise);
}
issueRedirect(request, response, redirectUrl);
return false;
}
}
- 自定义BosRealm
public class UserRealm extends AuthorizingRealm{
private static Logger log = LoggerFactory.getLogger(UserRealm.class);
@Autowired
SysUserInfoServiceImpl userService;
/**
* 登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
log.info("登录认证");
// 通过表单接收的用户名
String username = token.getUsername();
String password = token.getPassword().toString();
if (token.getPassword() != null) {
password = new String(token.getPassword());
}
SysUserInfo user = userService.selectUserByUserName(username);
if (user != null) {
//是否通过 域认证
//boolean flag = ldapAuthentication.authentication(username, password);
boolean flag = username.equals(user.getSysUsername())&&password.equals(user.getSysPwd());/**/
if (flag) {
// 需要验证用户是否在本环境中存在
//SysUserInfo user = userService.selectUserByUserName(username);
/* if (user == null) { // 如果没有此用户, 则添加一个用户
user = userService.addNewUser(username);
} else if (user.getIsDeleted()) {
throw new UnknownAccountException("已通过域认证,用户被锁定,请联系管理员。");// 账号锁定
}
if (user == null) {
throw new UnknownAccountException("已通过域认证,创建用户失败。");
}*/
//如果身份认证验证成功,返回一个AuthenticationInfo实现;
SimpleAuthenticationInfo info = null;
try {
info = new SimpleAuthenticationInfo(user, password.toCharArray(), user.getSysUsername());
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return info;
} else {
log.info(username+"用户名或密码错误。");
throw new UnknownAccountException("用户名或密码错误");
}
}else{
log.info(username+"用户名不存在");
throw new UnknownAccountException("用户名或密码错误");
}
}
}
- 登录controller,LoginController
@Controller
public class LoginController {
private static Logger log = LoggerFactory.getLogger(LoginController.class);
/**
* 访问需要登录的页面时被拦截后,需要跳转到到login页面
* 结合shiro配置查看
* @throws IOException
*/
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException {
log.warn("shiro拦截到需要登录的请求: "+ httpServletRequest.getRequestURI());
Subject subject = SecurityUtils.getSubject();
boolean loginStatus = subject.isAuthenticated();
if (loginStatus) {
log.warn("已登录状态,返回到主页");
Cookie coo = new Cookie("loginMessage", null);
httpServletResponse.addCookie(coo);
return "redirect:login/index";
} else {
log.warn("未登录状态,返回登录页面");
Cookie cook = new Cookie("loginMessage", null);
httpServletResponse.addCookie(cook);
return "/pages/login";
}
}
@RequestMapping(value = "login/index")
public String toindex(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) {
log.warn("登录成功,跳转到主页");
return "/pages/index";
}
/**
* 登录失败会调用此接口,返回登录界面
* @throws IOException
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String loginFail(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException{
Subject subject = SecurityUtils.getSubject();
boolean loginStatus = subject.isAuthenticated();
if (loginStatus) {
log.warn("已登录状态,返回到主页");
Cookie coo = new Cookie("loginMessage", null);
httpServletResponse.addCookie(coo);
return "redirect:/index";
} else {
//登录失败了 提取错误消息
Exception shiroLoginFailureEx = (Exception) httpServletRequest.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);
String errorMsg = shiroLoginFailureEx.getMessage();
log.warn("登录失败后返回login页面");
Cookie coo = new Cookie("loginMessage", URLEncoder.encode(errorMsg,"UTF-8"));
httpServletResponse.addCookie(coo);
return "/pages/login";
}
}
}
顺序:提交表单后-->CustomFormAuthenticationFilter-->UserRealm-->LoginController(/login)