Shiro Filters

1. Overview
对于web应用,Shiro提供了认证和授权的过滤器实现。

2. Architecture
[img]http://dl.iteye.com/upload/attachment/0083/9506/b022d723-8c47-3c5b-98d4-87d210448a4c.jpg[/img]
a. ServletContextSupport
提供了对ServletContext访问的支持。
b. AbstractFilter
提供了对过滤器初始化的配置。如果想在过滤器初始化时添加逻辑,可通过覆写onFilterConfigSet方法。
c. NameableFilter
提供了重命名过滤器的单一功能。过滤器默认的名字为web.xml中配置的filter-name,可通过NameableFilter的name属性设置一个新的名字。
d. OncePerRequestFilter
每次请求,过滤器需要依次执行,OncePerRequestFilter保证了每次请求时,每个过滤器只执行一次。实际的业务逻辑,由子类通过doFilterInternal方法实现。
e. AdviceFilter
覆写了OncePerRequestFilter的doFilterInternal方法,将过滤操作细化为前置处理,后置处理以及清理操作。只有前置方法preHandle返回true时,才会调用下一个过滤器。
f. PathMatchingFilter
支持Ant风格路径匹配。如果请求的路劲与配置的路径匹配,由onPreHandle方法决定该请求将如何处理。对于过滤器AnonymousFilter的实现,onPreHandle方法总是返回true。对于过滤器AccessControlFilter,需要由isAccessAllowed和onAccessDenied方法共同决定。所有的认证和授权过滤器都继承AccessControlFilter类。

3. Authentication Filters
[img]http://dl.iteye.com/upload/attachment/0083/9776/6fd354ed-e40c-30f6-9b87-03b41d02ce5d.jpg[/img]
AuthenticationFilter为所有认证过滤器的基类,默认的isAccessAllowed方法返回subject.isAuthenticated的值。也就是说,如果请求的路径配置了authc过滤器,需要当前的subject被认证过。如果当前的subject没有被认证过,不同的实现,执行的操作有所不同。BasicHttpAuthenticationFilter实现会根据请求的头信息自动执行一次认证操作,如果认证失败,请求返回401代码。FormAuthenticationFilter实现会根据当前的请求路径与登陆路径匹配,如果为登陆请求,尝试执行一次认证操作,如果不为登陆请求,请求调转到登陆页。

3.1 AnonymousFilter
AnonymousFilter主要用于那些不需要认证即可访问的资源。

[urls]
/user/signup/** = anon
/user/** = authc

对于/user/signup,任何用户都可以访问。对于/user/,需要当前的subject已认证过。

3.2 UserFilter
如果配置了UserFilter,表示请求的Subject已认证过或被"remember me"记忆:

[urls]
/news/**=user

对于/news/下的所有资源,只要用户登录过即可访问。

3.3 FormAuthenticationFilter
FormAuthenticationFilter需要当前的Subject必须被认证过,否则流程将跳转到设置的登陆url(通过setLoginUrl方法)。

[urls]
/news/**=authc


3.4 BasicHttpAuthenticationFilter
如果请求的Subject.isAuthenticated方法返回false,BasicHttpAuthenticationFilter将通过HTTP基础认证方式进行认证(关于HTTP基础认证,请查阅相关资料):

[urls]
/news/**=authcBasic

如果请求/news/,程序会执行Subject.isAuthenticated方法,如果返回false,执行HTTP基础认证。

4. Authorization Filters
[img]http://dl.iteye.com/upload/attachment/0083/9953/5e3c8290-48de-3482-8615-88b4d238c113.jpg[/img]
AuthorizationFilter为所有授权过滤器的基类。默认的onAccessDenied方法中逻辑为,如果当前的Subject没有绑定任何标识信息,将请求转向登陆页面。如果Subject绑定了标识信息并且配置了unauthorizedUrl,请求将转向unauthorizedUrl,否则返回401代码。

4.1 PermissionsAuthorizationFilter
常用的AuthorizationFilter实现为PermissionsAuthorizationFilter,支持以权限的形式配置资源:

[urls]
/system/getUsers=perms[0]

如果请求的路径为/system/getUsers,需要当前的Subject具有权限"0"(调用subject的isPermitted或isPermittedAll方法),才允许访问资源,否则不允许访问。

4.2 RolesAuthorizationFilter
RolesAuthorizationFilter过滤器支持以角色的形式配置资源:

[urls]
/system/getUsers=roles[admin]

如果请求的路径为/system/getUsers,只有当前的Subject具有"admin"角色(调用subject的hasAllRoles方法),才允许访问资源,否则不允许访问。

4.3 PortFilter
PortFilter支持以端口的形式配置资源:

[urls]
/some/path/** = port
/another/path/** = port[8080]

如果请求的端口号与配置的资源映射端口号相同,则允许对资源进行访问。否则请求将被转向为对应资源映射端口的路劲。比如:如果请求地址为/another/path/test1,端口为9090,由于/another/path/test1需要8080端口才能够访问,那么请求将会被转向:http://localhost:8080//another/path/test1,亦即被转向了正确的url路劲。

4.4 HostFilter
HostFilter支持以主机的形式控制对资源的访问,Shiro1.1还没有完全实现对这种形式的支持。其isAccessAllowed方法将返回UnsupportedOperationException异常。

4.5 SslFilter
SslFilter支持以ssl的形式配置资源:

[urls]
/secure/getUser = ssl

如果要访问资源/secure/getUser,需要请求的地址相同且必须为https,端口为443才允许访问。

4.6 HttpMethodPermissionFilter
HttpMethodPermissionFilter支持以HTTP请求方法的方式配置资源。其内部将HTTP请求方法转义为对应的HTTP操作,对应关系如下:
[table]
|head | read
|get | read
|put | update
|post | create
|mkcol | create
|options | read
|trace | read
[/table]
以下是一个例子:

[urls]
/user/** = rest[user]

如果使用GET方法访问/user/1234,由于GET方法对应着"read"操作,最后会转义为:Subject.isPermitted("user:read")。也就是说,当前的Subject必须具有user的read权限才允许对资源的访问。如果使用POST请求/user,会转义为Subject.isPermitted("user:create"),即当前Subject必须具有user的create权限。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
web.xml配置 因为我们是与spring进行集成的,而spring的基本就是web项目的xml文件。所以我们在web.xml中配置shiros的过滤拦截。正常情况下,我们需要将shirofilter配置在所有的filter前面,当然和encodingFilter这个filter是不区分前后的。因为两者互相不影响的。spring-shiro.xml 这里我们将来看看spring-shiro.xml的配置,这里我采取倒叙的方式讲解,我觉的倒叙更加的有助于我们理解代码。首先我们还记得在web.xml中配置的那个filter吧,名字shiroFilter,对spring-shiro.xml配置文件就是通过这个filter展开的。首先我们在web.xml配置的过滤器实际上是配置ShiroFilterFactoryBean,所以在这里需要将ShiroFilterFactoryBean定义为shiroFilter <!-- Shiro的核心安全接口,这个属性是必须的 --> <!-- 要求登录时的链接(可根据项目的URL进行替换),非必须的属性,默认会自动寻找Web工程根目录下的"/login.html"页面 --> <!-- 登录成功后要跳转的连接 --> <!-- 用户访问未对其授权的资源时,所显示的连接 --> <!-- 若想更明显的测试此属性可以修改它的值,如unauthor.jsp,然后用[玄玉]登录后访问/admin/listUser.jsp就看见浏览器会显示unauthor.jsp --> <!-- Shiro连接约束配置,即过滤链的定义 --> <!-- 此处可配合我的这篇文章来理解各个过滤连的作用http://blog.csdn.net/jadyer/article/details/12172839 --> <!-- 下面value值的第一个'/'代表的路径是相对于HttpServletRequest.getContextPath()的值来的 --> <!-- anon:它对应的过滤器里面是空的,什么都没做,这里.do和.jsp后面的*表示参数,比方说login.jsp?main这种 --> <!-- authc:该过滤器下的页面必须验证后才能访问,它是Shiro内置的一个拦截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter --> /statics/**=anon /login.html=anon /sys/schedule.html=perms[sys:schedule:save] /sys/login=anon /captcha.jpg=anon /**=authc
Shiro可以与JWT(JSON Web Token)结合使用来实现认证和授权功能。具体步骤如下: 1.编写JWT工具类,用于生成和验证JWT token。 2.编写JWTToken,继承Shiro的AuthenticationToken接口,用于封装JWT token。 3.编写JWTFilter,继承Shiro的AuthenticatingFilter类,用于在请求中验证JWT token。 4.编写JWTRealm,继承Shiro的AuthorizingRealm类,用于从JWT token中获取用户信息并进行授权。 5.编写Shiro配置类,配置JWTFilter和JWTRealm。 下面是一个简单的示例代码: ```java // JWT工具类 public class JWTUtil { // 生成JWT token public static String createToken(String username, String secret) { Date now = new Date(); Date expireDate = new Date(now.getTime() + 3600 * 1000); // 过期时间为1小时 return Jwts.builder() .setSubject(username) .setIssuedAt(now) .setExpiration(expireDate) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } // 验证JWT token public static boolean verifyToken(String token, String username, String secret) { try { Claims claims = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); return claims.getSubject().equals(username) && !claims.getExpiration().before(new Date()); } catch (Exception e) { return false; } } } // JWTToken public class JWTToken implements AuthenticationToken { private String token; public JWTToken(String token) { this.token = token; } @Override public Object getPrincipal() { return token; } @Override public Object getCredentials() { return token; } } // JWTFilter public class JWTFilter extends AuthenticatingFilter { @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { String token = getRequestToken((HttpServletRequest) request); if (StringUtils.isBlank(token)) { return null; } return new JWTToken(token); } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { String token = getRequestToken((HttpServletRequest) request); if (StringUtils.isBlank(token)) { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setHeader("Access-Control-Allow-Credentials", "true"); httpResponse.setHeader("Access-Control-Allow-Origin", ((HttpServletRequest) request).getHeader("Origin")); httpResponse.setCharacterEncoding("UTF-8"); httpResponse.setContentType("application/json"); httpResponse.getWriter().print("{\"code\":401,\"msg\":\"未登录\"}"); return false; } return executeLogin(request, response); } private String getRequestToken(HttpServletRequest request) { String token = request.getHeader("Authorization"); if (StringUtils.isBlank(token)) { token = request.getParameter("token"); } return token; } } // JWTRealm public class JWTRealm extends AuthorizingRealm { @Override public boolean supports(AuthenticationToken token) { return token instanceof JWTToken; } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = JWTUtil.getUsername(principals.toString()); // 根据用户名获取用户角色和权限信息 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setRoles(user.getRoles()); info.setStringPermissions(user.getPermissions()); return info; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException { String token = (String) auth.getCredentials(); String username = JWTUtil.getUsername(token); // 根据用户名获取用户信息 if (user == null) { throw new UnknownAccountException("用户不存在"); } if (!JWTUtil.verify(token, username, user.getPassword())) { throw new IncorrectCredentialsException("token无效"); } return new SimpleAuthenticationInfo(token, token, "jwtRealm"); } } // Shiro配置类 @Configuration public class ShiroConfig { @Bean public JWTFilter jwtFilter() { return new JWTFilter(); } @Bean public JWTRealm jwtRealm() { return new JWTRealm(); } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(jwtRealm()); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager, JWTFilter jwtFilter) { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(securityManager); Map<String, Filter> filters = new HashMap<>(); filters.put("jwt", jwtFilter); shiroFilter.setFilters(filters); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/**", "jwt"); shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilter; } } // 异常处理类 @RestControllerAdvice public class ExceptionHandler { @ExceptionHandler(UnknownAccountException.class) public Result handleUnknownAccountException(UnknownAccountException e) { return Result.error("用户不存在"); } @ExceptionHandler(IncorrectCredentialsException.class) public Result handleIncorrectCredentialsException(IncorrectCredentialsException e) { return Result.error("密码错误"); } @ExceptionHandler(AuthenticationException.class) public Result handleAuthenticationException(AuthenticationException e) { return Result.error("认证失败"); } @ExceptionHandler(Exception.class) public Result handleException(Exception e) { return Result.error("系统异常"); } } // Controller @RestController public class UserController { @PostMapping("/login") public Result login(String username, String password) { // 根据用户名和密码验证用户信息 String token = JWTUtil.createToken(username, password); return Result.success(token); } @GetMapping("/user") public Result getUser() { // 获取当前用户信息 return Result.success(user); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值