Apache Shiro 是一个强大易用的 Java 安全框架,提供了认证、授权、加密和会话管理等功能,对于任何一个应用程序,Shiro 都可以提供全面的安全管理服务。并且相对于其他安全框架,Shiro 要简单的多。
Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。Shiro 可以帮助我们完成:认证、授权、加密、会话管理、与 Web 集成、缓存等。这不就是我们想要的嘛,而且 Shiro 的 API 也是非常简单;其基本功能点如下图所示:
组件 | 描述 |
---|---|
Authentication | 身份认证 / 登录,验证用户是不是拥有相应的身份 |
Authorization | 授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情,常见的如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限 |
SessionManager | 会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的。
Web环境中可使用DefaultWebSessionManager。
|
SecurityManager | 安全管理器,即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互。
Web环境中可使用DefaultWebSecurityManager。
|
Realm | 域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。 |
Subject | 主体,代表了当前 “用户” |
SessionDAO | 用于进行session序列化于反序列化的组件 |
CacheManager | 用于进行缓存管理的组件 |
Cache | 用于直接进行缓存操作的组件 |
子包 | 描述 |
---|---|
com.brillilab.ssmdemo.common.shiro.cache | 自定义缓存组件,用于进行认证信息、权限信息和会话信息的缓存操作 |
com.brillilab.ssmdemo.common.shiro.filter | 自定义权限过滤器 |
com.brillilab.ssmdemo.common.shiro.listenter | 自定义会话监听器 |
com.brillilab.ssmdemo.common.shiro.realms | 自定义Realms |
com.brillilab.ssmdemo.common.shiro.session | 自定义Session缓存管理组件,用于进行Sesssion缓存的操作,主要组件为SessionDAO和SessionRepository |
com.brillilab.ssmdemo.common.shiro.token | 自定义Token,使用不同的自定义Token可以实现不数据结构的Token |
com.brillilab.ssmdemo.common.shiro.utils | 相关工具包 |
/common/** = anon
/login = anon
/index = anon
/user/insert = login,permission
/user/delete = login,permission
/logout = anon
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
shiroFilter
/*
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String login(User user, Boolean rememberme, HttpServletRequest request, HttpServletResponse response){
try {
//1.获取认证对象
Subject subject = SecurityUtils.getSubject();
//2.实例化Token对象
String sessionId=request.getRequestedSessionId();
String username=user.getUsername();
String password=user.getPassword();
UsernamePasswordToken token=new UsernamePasswordToken(username,password,rememberme);
//3.进行登录认证
subject.login(token);
}catch (Exception e){
return "error/loginerror";
}
return "redirect:/index";
}
@RequestMapping("/logout")
public String logout(HttpServletRequest request, HttpServletResponse response){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/index";
}
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Autowired
RoleService roleService;
@Autowired
PermissionService permissionService;
/**
* 授权信息,用于进行权限认证
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//(1)获得签名, 可做进一步查询
User token= (User) principalCollection.getPrimaryPrincipal();
User user=userService.login(token);
//(2)创建SimpleAuthenticationInfo对象
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//(3)查询角色、权限信息
Set roles = roleService.findRolesByUser(user);
info.setRoles(roles);
Set permissions = permissionService.findPermissionsByUser(user);
info.addStringPermissions(permissions);
return info;
}
/**
* 认证信息,主要针对用户登录,
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//(1)获取登录的token
UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
//(2)获取username和password,调用登录
String username=token.getUsername();
String password=new String(token.getPassword());
User user=new User();
user.setUsername(username);
user.setPassword(password);
User login = userService.login(user);
//(3)登录失败
if (null==login){
throw new AccountException("账号或密码不正确!");
}
if (login.getStatus()==0){
throw new DisabledAccountException("帐号已经禁止登录!");
}
//(4)登录成功
login.setUpdatetime(new Date());
userService.updateUser(login);
return new SimpleAuthenticationInfo(login,login.getPassword(),login.getUsername());
}
}
Subject subject = SecurityUtils.getSubject();
List roles = Arrays.asList(new String[]{"admin", "super"});
subject.hasAllRoles(roles);
subject.hasRole("admin");
在JSP页面引入Shiro标签ku
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
/common/** = anon
/login = anon
/index = anon
/user/insert = login,permission
/user/delete = login,permission
1.LoginFilter
public class LoginFilter extends AccessControlFilter {
/**
* 写如何能通过该拦截器的逻辑
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object o) throws Exception {
//(1)获取认证信息
User token = (User)SecurityUtils.getSubject().getPrincipal();
//(2)登录请求直接放行
if(null != token || isLoginRequest(request, response)){
return true;
}
//(3)非登录请求,重定向到登录页面
ResultVo result = ResultVoUtil.failed("当前用户没有登录!");
WebUtil.jsonOut(response,result);
return false;
}
/**
* isAccessAllowed返回值为fales时经过该方法
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//保存Request和Response 到登录后的链接
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
2.RoleFilter
public class RoleFilter extends AccessControlFilter {
/**
* 写如何能通过该拦截器的逻辑
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object value) throws Exception {
//(1)获取认证实体
Subject subject = getSubject(request, response);
//(2)进行角色校验(角色为"1"能通过)
if (subject.hasAllRoles(Arrays.asList("1"))){
return true;
}
//(3)未通过角色校验重定向回登录页面
((HttpServletResponse)response).sendRedirect(request.getServletContext().getContextPath()+"/login");
return false;
}
/**
* isAccessAllowed返回值为fales时经过该方法
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//(3)未通过角色校验重定向回登录页面
//((HttpServletResponse)response).sendRedirect(request.getServletContext().getContextPath()+"/login");
Subject subject = getSubject(request, response);
if (subject.getPrincipal()==null){
//未登录重定向登录页面
saveRequest(request);
WebUtils.issueRedirect(request,response,((HttpServletRequest)request).getContextPath()+"/login");
}else {
//已登录重定向未授权错误页面
WebUtils.issueRedirect(request,response,((HttpServletRequest)request).getContextPath()+"/error/unauthorizerror");
}
return false;
}
}
3.PermissionFilter
public class PermissionFilter extends AccessControlFilter {
/**
* 写如何能通过该拦截器的逻辑
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object o) throws Exception {
//(1)获取认证实体
Subject subject = getSubject(request,response);
//(2)进行权限校验
//①获取请求url
HttpServletRequest httpRequest=(HttpServletRequest)request;
String url = httpRequest.getRequestURI();
String contextPath = httpRequest.getContextPath();
if (url!=null && url.startsWith(contextPath)){
url = url.replaceFirst(contextPath,"");
}
//②权限校验
if (subject.isPermitted(url)){
return true;
}
return false;
}
/**
* isAccessAllowed返回值为fales时经过该方法
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//(3)未通过权限校验的逻辑
Subject subject = getSubject(request, response);
if (subject.getPrincipal()==null){
//未登录重定向登录页面
saveRequest(request);
WebUtils.issueRedirect(request,response,((HttpServletRequest)request).getContextPath()+"/login");
}else {
//已登录重定向未授权错误页面
WebUtil.jsonOut(response,ResultVoUtil.failed("用户未被授权该操作"));
}
return false;
}
}