Spring Boot中的Shiro相关内容

标签: Springboot 分类: Java 创建时间:2020-04-11 08:18:35 更新时间:2024-11-15 10:49:43

在上一篇文章 Post not found: Spring Boot集成shiro步骤 Spring Boot集成shiro步骤 中,我简单的写了一下在SpringBoot中集成Shiro的步骤。年代比较久远了,而且当时也是刚刚学习Shiro框架,不太懂,时隔很长时间后,再次使用Shiro,然后争取能对Shiro的权限认证有一个更加深入的理解。

1.密码比对

在进行密码比对的时候,UsernamePasswordToken内部将密码部分转为字符数组,所以如果数据库中存储的是明文信息,那么就行比对的时候,就要将传入的信息进行转换一下toCharArray() 方法将数据库中的明文进行转换一下,才能进行正确的密码比对。

1
2
3
4
5
6
7
8
9
10
11
12
13
try{
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
userInfo.getUsername(), //用户名
userInfo.getPassword().toCharArray() , //数据库中的明文密码
getName() //realm name
);
return authenticationInfo;
}catch (Exception e){
System.out.println("授权失败");
logger.error("doGetAuthenticationInfo",userInfo);
return null;
}

在进行密码比对时的逻辑如下:

密码比对是在 SimpleCredentialsMatcher 类中的 doCredentialsMatch 方法中进行密码比对的,比对的方式也很简单,直接使用了对用户输入的密码和数据库中的密码生成 byte 数组然后进行比较,最终的比较在 MessageDigest 类的 isEqual 方法中。部分逻辑如下:

2.SessionManage

3.ajax拦截

关于ajax请求的判断,有人说使用请求头里面的X-Requested-With,但是像普通的ajax是没有这个请求的。

4.shiro和OAuth2

5.自定义过滤器

我自定义了一个过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class FormLoginFilter extends PathMatchingFilter {
@Override
protected boolean onPreHandle(ServletRequest request,ServletResponse response, Object mappedValue) throws Exception {
Subject subject = SecurityUtils.getSubject();
boolean isAuthenticated = subject.isAuthenticated();
HttpServletResponse resp = (HttpServletResponse) response;
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
return true;
}

// 未授权请求
if (!isAuthenticated) {
// 返回状态码
resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
// 中文乱码
resp.setContentType("text/html;charset=UTF-8");
resp.getWriter().print("{\"state\":\"403\",\"msg\":\"未授权\"}");
return false;
}
return true;
}
}

然后注入到shiro中,本意是想让所有需要授权的接口,都走这个过滤器,而不需要认证的接口,都不走这个过滤器链,比如/login.但是似乎是事与愿违,所有的请求,都会走这个过滤器,造成了无法估量的损失。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@Configuration
public class BaseSecurityConfig {
@Bean
public FormLoginFilter formLoginFilter(){
return new FormLoginFilter();
}

//配置shrio拦截器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

// 必须设置SecuritManager
shiroFilterFactoryBean.setSecurityManager(securityManager);

Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
//配置拦截器,实现无权限返回401,而不是跳转到登录页
filters.put("authc", formLoginFilter());
// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
shiroFilterFactoryBean.setLoginUrl("/");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/");
// 未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/403");

// 拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
// 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
// authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
filterChainDefinitionMap.put("/static/**","anon");
filterChainDefinitionMap.put("/user/register", "authc");
filterChainDefinitionMap.put("/user/checkloginByCode", "anon");
filterChainDefinitionMap.put("/user/**", "anon");
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/loginWX", "anon");
filterChainDefinitionMap.put("/get_auth_access_token", "anon");
filterChainDefinitionMap.put("/scan", "anon");
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

return shiroFilterFactoryBean;
}
}


我尝试使用参考文章中的方法,使用如下的内容进行取消注入,但是失效了。

1
2
3
4
5
6
@Bean
public FilterRegistrationBean registration(FormLoginFilter filter) {
FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}

最后只能通过继承ApplicationContextAware接口,通过手动的方式获取Bean,比如参考文章4中说到的一样。至于如何获取到@Value中的值,那就和参考文章7中的一样就好了。SpringContextUtil工具类如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext = applicationContext;
}

/**
* 获取上下文
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}

/**
* 通过 bena 名称获取上下文中的 bean
*/
public static Object getBean(String name) {
return applicationContext.getBean(name);
}

/**
* 通过类型获取上下文中的bean
*/
public static Object getBean(Class<?> requiredType) {
return applicationContext.getBean(requiredType);
}
/**
* 获取application.yml中的属性
*/
public static String getValue(String name){
return applicationContext.getEnvironment().getProperty(name); // 方法2
}

}

参考文章:
1.SpringBoot 整合Shiro 之 自定义Filter
2.Shiro-实战(四)—过滤器机制 (写了各种拦截器的作用)
3.Shiro 自定义 filter 匹配异常(好文) (这篇文章中出现了同样的问题,还有问题的方法求解过程,但是最后的结论有点差强人意,就是说不要把自定义的filter注册为Bean,也就是使用filters.put(“authc”, new FormLoginFilter()),代替filters.put(“authc”, formLoginFilter()),这显然不符合我们的预期,因为我要在Filter中使用其他的Bean)
4.Spring Boot 自定义 Shiro 过滤器,无法使用 @Autowired 解决方法 (这篇文章也是修改了注入方法,同样出现了问题,于是就使用了创建的SpringContextUtil工具类,手动获取Bean的方法,来使用@Autowired,这是是解决我问题的主要方法)
5.springboot2整合shiro时@value注解无效解决方案 (这里是讲如何在过滤器中使用@Value进行属性注入的方法,主要是将生命周期静态化,但是这中方法很不适用,或者说与我的需求有些不符合)
6.Spring拦截器中@Value无效的解决办法 (这个是将Filter当作Bean注入到Spring中,与我的问题相冲突)
7.不用@Value从Spring的ApplicationContext中获取一个或全部配置 (这里是如何使用applicationContext读取application.yml中的配置,方法还是很有用的)
8.filter过滤器使用Autowired注入Bean为null (在init方法中使用filterConfig参数,如果在dofilter方法中也可以使用request参数,这是两种获取Bean的方法,都是借助于WebApplicationContextUtils.getWebApplicationContext这个方法)
小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。