Spring+Shiro+Token认证

首先引入Shiro的依赖包

org.apache.shiro shiro-spring 1.3.2

在我们的wed.xml中加入我们的shiro过滤器
shiroFilter org.springframework.web.filter.DelegatingFilterProxy shiroFilter /*

【Spring+Shiro+Token认证】在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 里的 ? 后面的参数
配置完后当用户访问的时候,访问的流程如下:
  1. AesFilter:判断用户是否带有token,若有进入Reaml
  2. UserRealm :通过token认证获取到User用户
  3. UserRealm :通过User用户拿到权限,进行权限认证。
  4. 通过如perms[user] 、permissionOr[admin:staff_worker]等过滤器。
  5. 成功访问地址

    推荐阅读