首先引入Shiro的依赖包
org.apache.shiro
shiro-spring
1.3.2
在我们的wed.xml中加入我们的shiro过滤器
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
shiroFilter
/*
在Spring配置Shiro中的文件
/login/auth.do=anon
/worker/**=aesToken,permissionOr[admin:staff_worker,admin:staff]
/admin/**=aesToken,permissionOr[admin:staff_admin,admin:staff]
/payConfig/**=aesToken
/login/out.do=logout
/**/**=aesToken,user
自定义token类
import org.apache.shiro.authc.AuthenticationToken;
/**
* Shiro中的Authentication,主要是检验token时使用
*/
public class AesToken implements AuthenticationToken{
String token;
public AesToken(String token){
this.token=token;
}
public Object getPrincipal() {
// TODO Auto-generated method stub
return token;
}
public Object getCredentials() {
// TODO Auto-generated method stub
return token;
}
}
创建自定义的shiro过滤器,当过滤器return true时返回到shiro权限认证的自定义realm中,把token放在头部,如果检查到没有token的话则视为未登录状态。有token的情况下会根据token来进行用户的权限认证。
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.json.JSONObject;
import com.southgis.iznaer.base.ResponseConstant;
import com.southgis.iznaer.base.ResponseResult;
import com.southgis.iznaer.util.AesToken;
/**
* 通过改过滤器跳到Realm中认证用户信息和权限信息
* @author gyc
* @date 2018-10-18
*/
public class AesFilter extends BasicHttpAuthenticationFilter {
/**
* 过滤方法
*/
protected boolean isAccessAllowed(ServletRequest request,
ServletResponse response, Object object) throws AuthenticationException {
String token=((HttpServletRequest) request).getHeader("token");
ResponseResult jsonResult = new ResponseResult();
//判断请求的请求头是否带上 "Token"
if (token!=null) {
try {
AesToken m=new AesToken(token);
getSubject(request, response).login(m);
return true;
} catch (AuthenticationException e) {
//token 错误
e.printStackTrace();
jsonResult.setCode(ResponseConstant.TOKEN_FAIL_CODE);//返回失败的code
jsonResult.setState(ResponseConstant.FAIL_STATE);//返回失败的state
jsonResult.setDescription("token错误,或token已过期");
jsonResult.setResults(null);
JSONObject jsonObject=new JSONObject(jsonResult);
responseOutWithJson(response,jsonObject.toString());
return false;
}
}
//如果请求头不存在 Token,则可能是执行登陆操作或者是游客状态访问,无需检查 token,直接返回 true
return true;
}
/**
* 以JSON格式输出
* @param response
*/
protected void responseOutWithJson(ServletResponse response,
Object responseObject) {
//将实体对象转换为JSON Object转换
// JSONObject responseJSONObject = JSONObject.fromObject(responseObject);
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
try {
out = httpServletResponse.getWriter();
out.append((String)responseObject);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
}
}
}
以下两个也是自定的权限过滤器,功能类似shiro中的默认权限。
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
/**
* 自定义权限过滤器,拥有多权限之一的用户可以通过
* @author gyc
* @date 2018-10-18
*/
public class PermissionOrFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest req,
ServletResponse resp, Object object) throws Exception {
Subject subject = getSubject(req, resp);
String[] permissions = (String[]) object;
if (permissions == null || permissions.length == 0) {
return true;
}
for (String permission : permissions) {
if (subject.isPermitted(permission)) {
return true;
}
}
return false;
}
}
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
/**
* 自定义角色过滤器,拥有多角色之一的用户可以通过
* @author gyc
* @date 2018-10-18
*/
public class RolesOrFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest req,
ServletResponse resp, Object object) throws Exception {
Subject subject = getSubject(req, resp);
String[] roles = (String[]) object;
if (roles == null || roles.length == 0) {
return true;
}
for (String role : roles) {
if (subject.hasRole(role)) {
return true;
}
}
return false;
}
}
自定义Reaml,在上文中我们所设置的自定义的shiro过滤器AesFilter 通过后会跳到Reaml中根据我们的token来进行权限的认证和分配。
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class UserRealm extends AuthorizingRealm {
User user=null;
@Resource(name = "userServiceImpl")
UserService userService;
@Resource(name = "permissionServiceImpl")
PermissionService permissionService;
/**
* 必须重写此方法,不然会报错
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof AesToken;
}
/**
* 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException{
System.out.println("————身份认证方法————");
String token = (String) authenticationToken.getCredentials();
user=userService.findUserByToken(token);
if(this.user== null){
throw new AuthenticationException("token认证失败!");
}
return new SimpleAuthenticationInfo(token, token, getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("————权限认证————");
permissionService.findPermissionByUser(user);
Set permissionSet = new HashSet();
for (Permission permission : permissions) {
permissionSet.add(permission.getName());
}
info.setStringPermissions(permissionSet);
//把permissionSet放入info,info成功return后就可以成功配置和认证权限
return info;
}
}
到这里我们的shiro配置算是配置完毕了。
配置完以后我们可以到刚刚配置关于shiro那一段的spring xml文件配置我们访问的路径所需要的权限。如下:
/login/auth.do=anon
/worker/**=aesToken,permissionOr[admin:staff_worker,admin:staff]
/admin/**=aesToken,permissionOr[admin:staff_admin,admin:staff]
/payConfig/**=aesToken
/login/out.do=logout
/**/**=aesToken,user
除了可以加入我们自定义的过滤器以外,我们还可以添加shiro默认的过滤器。
Filter | 解释 |
---|---|
anon | 无参,开放权限,可以理解为匿名用户或游客 |
authc | 无参,需要认证 |
logout | 无参,注销,执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(); 设置的 url |
authcBasic | 无参,表示 httpBasic 认证 |
user | 无参,表示必须存在用户,当登入操作时不做检查 |
ssl | 无参,表示安全的URL请求,协议为 https |
perms[user] | 参数可写多个,表示需要某个或某些权限才能通过,多个参数时写 perms[“user, admin”],当有多个参数时必须每个参数都通过才算通过 |
roles[user] | 参数可写多个,表示是某个或某些角色才能通过,多个参数时写 roles[“admin,user”],当有多个参数时必须每个参数都通过才算通过 |
rest[user] | 根据请求的方法,相当于 perms[user:method],其中 method 为 post,get,delete 等 |
port[8081] | 当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString 其中 schmal 是协议 http 或 https 等等,serverName 是你访问的 Host,8081 是 Port 端口,queryString 是你访问的 URL 里的 ? 后面的参数 |
配置完后当用户访问的时候,访问的流程如下: