之前我们了解了Shiro 认证的大致过程:
- Shiro将用户的信息封装到Token。
- 交给Subject对象,Subject对象将信息传递给核心组将SecurityManager中的Authenticator进行验证。
- Authenticator会跟Realm所获取的用户信息与Subject传递的用户信息进行对比认证。
- 结果返回。
Shiro还提供了记住我,密码加盐等功能。
Shiro认证的实现流程:
- 创建Controller,Service,Mapper基础框架。
- 创建自定义Realm(相当于数据源),关键是创建AuthenticationInfo对象,用于获取数据库或者缓存中的用户信息(用来于Subject获取到的信息进行比较)。
- 创建ShiroConfig(Shiro配置信息),主要配置加载自定义的realm文件,密码加密方式,加密的迭代次数等。
前后端分离项目如何使用
shiro想用于前后端分离项目,因为当用户未登录时后端需要返回一个json数据给前端,让前端判断跳转页面,但这时使用shiro默认的形式就不行了,因为shiro默认下会去当前项目中找login页面,所以要重写Shiro的过滤器。
使用过程
1.自定义过滤器,继承Shiro的过滤器FormAuthenticationFilter,重写onAccessDenied方法。
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setContentType("application/json; charset=utf-8");
Map<String,String> map = new HashMap<>();
map.put("code",HttpStatus.FORBIDDEN.toString());
map.put("msg","未登录");
ObjectMapper objectMapper = new ObjectMapper();
String respJson = objectMapper.writeValueAsString(map);
resp.getWriter().write(respJson);
resp.getWriter().flush();
return false;
2.在配置文件ShiroConfig添加
@Bean("shiroFilterFactoryBean")
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
//1.创建过滤器工厂
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
Map<String,Filter> filters = shiroFilter.getFilters();
//配置自定义的shiro过滤器
//这里filters.put("authc", new MyFilter());map添加的key值必须为authc,之前我自定义是无效的。
filters.put("authc",new MyFormAuthenticationFilter());
shiroFilter.setFilters(filters);
//排除shiroFilter默认的页面访问路径
//shiroFiLter.setLoginUrL("/Login.htmL");//默认访问的首页面
//shiroFiLter.setSuccessUrL("/index.htmL");
//shiroFiLter.setUnauthorizedUrL("/");
//anon代表是匿名访问方式,比如index.htmL是匿名访问的即不区分用户、谁都可以访问
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/public/**", "anon");
filterMap.put("/favicon.ico", "anon");
filterMap.put("/public/plugins/**", "anon");
filterMap.put("/public/plugins/UE/jsp/**", "anon");
filterMap.put("/webjars/**", "anon");
filterMap.put("/api/**", "anon");
filterMap.put("/originInfo/**", "anon");
//swagger配置
filterMap.put("/swagger**", "anon");
filterMap.put("/v2/api-docs", "anon");
filterMap.put("/swagger-resources/configuration/ui", "anon");
filterMap.put("/login.html", "anon");
filterMap.put("/index.html", "anon");
filterMap.put("/json", "anon");
filterMap.put("/sys/login", "anon");
filterMap.put("/captcha.jpg","anon");//认证码
filterMap.put("/upload/ckUploadImg", "anon");
filterMap.put("/upload/**", "anon");
filterMap.put("/upload/ck/**", "anon");
//authc代表是必须认证访问的。/**代表是除上面配置以外的其它资源。
filterMap.put("/**", "authc");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
知识小tips:常用的过滤器
- anon:任何人都可以访问。
- authc:必须是登录之后才能进行访问,不包括remember me。
- user:登录用户才可以访问,包含remember me。
- logout:登出的时候访问。
例子:
//设置不认证可以访问的资源:即/myController/login该路径是任何人都可访问。
defaultShiroFilterChainDefinition.addPathDefinition("/myController/login","anon");
defaultShiroFilterChainDefinition.addPathDefinition("/login","anon");
//设置需要进行登录认证的拦截范围
//任何路径,都需要登录后才可访问
defaultShiroFilterChainDefinition.addPathDefinition("/**","authc");
//任何路径都需登录才可访问,包括记住我的功能
//添加存在用户的过滤器(rememberMe) antPath:路径,defintion:相关操作
defaultShiroFilterChainDefinition.addPathDefinition("/**","user");
//配置登出过滤器
defaultShiroFilterChainDefinition.addPathDefinition("/logout","logout");
这就有个问题了,/myController/login路径:设置为anon,但下面的又设置了authc 又设置了任何路径都需要登录才可访问。这不就冲突了吗?
解密: 那会不会与写入的排列顺序有关? 例如:
filterMap.put("/**", "authc"); (1)
filterMap.put("/login/**", "anon"); (2)
authc会比anon先生效,就会导致后面的anon无法生效,从而导致登录页面也必须被认证才能访问。
例子2:
filterMap.put("/login/**", "anon"); (1)
filterMap.put("/**", "authc"); (2)
这时anon会比anthc先生效,即使anthc设置了所有路径都需要登录时。登录页面都无需被认证。
结论:当某些路径有多重过滤器时,过滤器的生效顺序,会根据写入先后顺序生效。所以要让无需认证的路径,让任何人都能访问,就要排在前面,先生效,需要认证的排在后面,后生效。