注:该文基于springMVC已经配置好。
项目结构:
shiro所需jar包:
注意,由于shiro的主要作用就是拦截判断,所以我们不再需要springMVC的LoginInterceptor
和PermissionInterceptor
拦截器了。其余的springMVC配置的代码保持不变。
在web.xml里添加shiroFilter:该过滤器名字将与spring.xml里注入的过滤器名字一致。所以当有请求时进了web.xml里时,请求就会被该过滤器转到spring.xml里的shiro过滤器。
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
targetBeanName
shiroFilter
shiroFilter
/*
(本人将shiro的配置直接配在该文件下的,一般会新建一个shiro自己的配置文件,就像springMVC的springMVC-servlet.xml文件一样)
/refuse.jsp = anon
/login.do = authc
/logout.do = logout
/** = authc
securityManager:安全管理器。是必须注入的,所有的过滤都被它管理着。
loginUrl:没有登录时就会跳转到它指定的地址,如果不指定该属性,则会自动去找项目的根目录下的”/login.jsp”页面。
unauthorizedUrl:没有权限时默认的跳转路径。
filterChainDefinitions:过滤链。它可对路径进行过滤。如:
/refuse.jsp = anon anon过滤器表示认证不通过也可访问,所以这句代码表示refuse.jsp路径在没有登录时也可访问
/login.do = authc authc过滤器表示需要认证通过才可访问,也就是说login.do在没有登录时,是不能访问的,除非设为了loginUrl的值。
过滤器简称
类名
解释
示例
1.认证过滤器
anon
org.apache.shiro.web.filter.authc.AnonymousFilter
可匿名访问。无参,一般用于过滤静态资源
/admins/**=anon 表示admins下的所有路径皆可匿名访问`
authc
org.apache.shiro.web.filter.authc.FormAuthenticationFilter
认证登录后才可访问。
/admins/user/**=authc
authc属性
usernameParam:表单提交的用户名参数名( username); passwordParam:表单提交的密码参数名(password); rememberMeParam:表单提交的密码参数名(rememberMe);
loginUrl:登录页面地址(/login.jsp);successUrl:登录成功后的默认重定向地址;
failureKeyAttribute:登录失败后错误信息存储key(shiroLoginFailure);
authcBasic
org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
http身分认证器。无参
/admins/user/**=authcBasic
authcBasic属性
applicationName:弹出登录框显示的信息(application);
user
org.apache.shiro.web.filter.authc.UserFilter
表示必须存在用户
/admins/user/**=user 表示身份认证通过或通过记住我认证通过的可以访问,此时进行登录操作不会做检查
logout
org.apache.shiro.web.filter.authc.LogoutFilter
退出拦截器
/logout.do=logout
主要属性
redirectUrl:退出成功后重定向的地址(/)
2.授权过滤器
perms
org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
权限授权过滤器。有参,多参时写成拼接字符串,各参之间以逗号分隔。多参时需每个权限都通过时才算通过,相当于hasAllRoles()
单参:/admins/user/**=perms[add]
表示该路径只有add权限时才可访问。多参:/admins/user/**=perms["add,delete"]
port
org.apache.shiro.web.filter.authz.PortFilter
端口过滤器
/admins/user/**=port[8080] 当端口号不是8080时会将访问url的端口改为8080后再跳转。
主要属性
port(80):指定可以通过的端口
;
roles
org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
角色授权过滤器,可多参
属性
loginUrl:登录页面地址;
unauthorizedUrl:未授权后重定向的地址;
admins/user/**=roles[“admin,guest”] 多参时,都通过才算通过
rest
org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
请求方法拦截器(GET=read,POST=create,PUT=update,DELETE=delete,HEAD=read,TRACE=read,OPTIONS=read, MKCOL=create),它自动根据请求方法构建权限字符串。/admins/user/**=perms[user:post]会拼接“user:create”权限字符串
/users=rest[user],会自动拼出“user:read,user:create,user:update,user:delete”权限字符串进行权限匹配(所有都得匹配,isPermittedAll);
ssl
org.apache.shiro.web.filter.authz.SslFilter
安全协议拦截器,只有请求协议是https才能通过;否则自动跳转会https端口(443);其他和port拦截器一样;
/admins/user/**=ssl
此realm先不从数据库查询权限数据
/****
* 自定义Realm
*
* @author Peter
*
*/
public class MyShiroRealm extends AuthorizingRealm {
@Override
public String getName() {
return"customRealm";
}
// 支持什么类型的token
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
/***
* 获取授权信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
//先自定义一个query权限,就不从数据库查询了
String username = (String) pc.fromRealm(getName()).iterator().next();
if (username != null) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("query");//权限
return info;
}
return null;
}
/***
* 获取认证信息
*
* 问题:如何在登录后不再进登录页面了呢???????????????????????????
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken at) {
UsernamePasswordToken token = (UsernamePasswordToken) at;
// 通过表单接收的用户名
// String username = token.getUsername();
String username = (String) at.getPrincipal();
if (username != null && !"".equals(username)) {
User user = accountService.getUserByUserName(username);
if (user != null && user.getPassword().equals(new String(token.getPassword())) && user.getUsername().equals(token.getUsername())) {
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
}else{
//throw new UnknownAccountException(); //如果用户名错误
throw new IncorrectCredentialsException(); //如果密码错误
}
}
return null;
}
/**用户的业务类**/
private IAccountService accountService;
public IAccountService getAccountService() {
return accountService;
}
public void setAccountService(IAccountService accountService) {
this.accountService = accountService;
}
}
/***
* 提交数据的登录
*/
@RequestMapping(value = "login.do")
public String login(String username, String password,ModelMap model) {
Subject subject = SecurityUtils.getSubject();
if(subject.isAuthenticated()){
return "home.jsp";
}else{
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
String error = null;
try {
subject.login(token);
// subject.logout();
} catch (UnknownAccountException e) {
error = "用户名/密 错误";
} catch (IncorrectCredentialsException e) {
error = "用/密码 错误";
} catch (AuthenticationException e) {
//其他错误,比如锁定,如果想单独处理请单独catch处理
error = "其他错误:" + e.getMessage();
}
model.addAttribute("msg", error);
System.out.println("用户认证状态:"+subject.isAuthenticated());
if(subject.isAuthenticated()){
return "redirect:home";
}else{
return "/login.jsp";
}
}
}
由于shiro的sessionManager管理session,所以不用开发退出功能,直接使用shiro的logout拦截器即可退出。
/logout.do= logout
当用户无操作权限,shiro将跳转到refuse.jsp页面。
使用注解的方式赋的权限,首先看看controller里通过注解限定的权限:
/**
* 查询:注解query权限:只有当realm里查回的权限里有query时才可访问该方法
*
* 即访问该方法时,会先拿query权限去realm里查看授权时查回的权限里是否有query权限
* 有的话则允许访问,否则不允许
* @return
* @author Peter
*/
@RequestMapping(value = "query")
@RequiresPermissions("query")//指定访问的权限
public String query(){
System.out.println("查询内容");
return "/home.jsp";
}
当没有权限访问时,就会跳转到refuse.jsp页面,但是注解方式配置无权限跳转需要在springMVC的配置文件(springMVC-servlet.xml)里配,可能是因为shiro注解赋权限的配置在这里面吧。代码如下:
/refuse