From d4b083c023a5401abc8d84192e438fb6eaed72b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E6=B6=9B?= Date: Wed, 20 Oct 2021 15:41:30 +0800 Subject: [PATCH] =?UTF-8?q?enhance:=E6=9B=B4=E6=AD=A3Shiro=E6=96=87?= =?UTF-8?q?=E7=AB=A0=E4=B8=AD=E7=9A=84=E4=B8=80=E4=BA=9B=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/_posts/shiro-spring.md | 54 ++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/source/_posts/shiro-spring.md b/source/_posts/shiro-spring.md index 9b2c676..beb61e3 100644 --- a/source/_posts/shiro-spring.md +++ b/source/_posts/shiro-spring.md @@ -107,19 +107,13 @@ Token是一个用于承载用于用户认证信息的容器,常用的Token只 ```xml org.apache.shiro - shiro-spring - ${shiro.spring.version} - - - - org.apache.shiro - shiro-web + shiro-spring-boot-starter ${shiro.spring.version} ``` !!! info "" - 在本文进行编写的时候,Shiro-Spring的版本是1.8.0。 + 在本文进行编写的时候,`shiro-spring-boot-starter`的版本是1.8.0。你可能在Shiro的官方网站上看到了Shiro与Spring整合的说明,但是请注意,那片说明是针对Shiro与Spring应用整合的,而不是用于Spring Boot应用的。另外,在网上许多教程与介绍中,都建议使用`shiro-spring-boot-web-starter`,但是通过对Shiro源码的查看,`shiro-spring-boot-web-starter`已经合并进了`shiro-spring-boot-starter`,所以直接选用最短的那个就好。 ### 认证与授权功能流程 @@ -132,6 +126,9 @@ Token是一个用于承载用于用户认证信息的容器,常用的Token只 !!! info "" 其实对于用户认证处理最核心的就是,只要调用`subject.login(token)`时不抛出任何异常,那么用户就已经被系统认证通过了,可以在之后的操作中通过获取`Subject`实例来得到用户的相关信息。 +!!! caution "" + 需要注意的是,`shiro-spring-boot-starter`中对于Bean的引用大部分都是通过Bean名称的,所以如果在运行Spring应用的时候,提示缺少再买一个名称的Bean,那么可以检查一下应用中的Shiro配置,看看是不是已经定义了相应类型的Bean,但是没有使用Shiro需求的名字。这样的话可以直接给应用中的Bean重新起一个名字即可。 + ### Shiro配置 对Shiro的配置,主要是配置Shiro的处理流程,通过定义其中的关键部件,使上图中的流程完全打通。在加入其他的功能类之前,可以首先先在项目中添加一个仅有基本功能的配置类。 @@ -249,7 +246,7 @@ public List authenticationRealms() { ```java @Component -public class MultiRealmsSecurityManager extends DefaultSecurityManager { +public class MultiRealmsSecurityManager extends DefaultWebSecurityManager { public MultiRealmsSecurityManager( SubjectDAO subjectDAO, @@ -270,6 +267,9 @@ public class MultiRealmsSecurityManager extends DefaultSecurityManager { } ``` +!!! info "" + 因为所构建的应用是一个提供Web服务的应用,所以需要继承`DefaultWebSecurityManager`来提供对于Web的安全控制。 + #### 构建对用户名与密码的验证 首先假设应用中的用户表结构是如下面这样定义的。 @@ -321,6 +321,11 @@ public class Member { public class UsernameRealm extends AuthoringRealm { private final MemberRepository memberRepository; + @PostConstruct + private void initRealm() { + super.setCredentialsMatcher(new HashedCredentialsMatcher("SHA-512")); + } + @Override public boolean supports(AuthenticationToken token) { return token instanceof UsernamePasswordToken; @@ -368,7 +373,7 @@ public class TokenStore { @Column(nullable = false) LocalDateTime expiresAt; - @OneToOne(targetEntity = Member.class, fetch = FetchType.LAZY, cascade = CascadeType.MERGE) + @OneToOne(targetEntity = Member.class, fetch = FetchType.EAGER, cascade = CascadeType.MERGE) @JoinColumn(name = "mid") Member member; } @@ -383,6 +388,11 @@ public class TokenRealm extends AuthorizingRealm { private final MemberRepository memberRepository; private final TokenStoreRepository storeRepository; + @PostConstruct + private void initRealm() { + super.setCredentialsMatcher(new SimpleCredentialsMatcher()); + } + @Override public boolean supports(AuthorizationToken token) { return token instanceof BearerToken; @@ -439,7 +449,7 @@ public class WebShiroConfig { SecurityUtils.setSecurityManager(this.securityManager); } - @Bean + @Bean(name = "shiroFilterFactoryBean") @Autowired public ShiroFilterFactoryBean shiroFilterFactory( ShiroFilterChainDefainition filterChainDefinition @@ -447,10 +457,6 @@ public class WebShiroConfig { var filterFactory = new ShiroFilterFactoryBean(); filterFactory.setSecurityManager(this.securityManager); - Map filters = new HashMap<>(); - filters.put("bearer", bearerFilter()); - filterFactory.setFilters(filters); - filterFactory.setLoginUrl("/login"); filterFactory.setFilterChainDefinitionMap(filterChainDefinition.getFilterChainMap()); return filterFactory; @@ -464,13 +470,29 @@ public class WebShiroConfig { chainDefinition.addPathDefinition("/logout", "logout"); chainDefinition.addPathDefinition("/login", "anon"); // 要求所有需要携带用户令牌的请求都必须经过BearerHttpAuthenticationFilter - chainDefinition.addPathDefinition("/**", "bearer"); + chainDefinition.addPathDefinition("/**", "authcBearer"); return chainDefinition; } } ``` +#### 集成Shiro后常见错误的处理 + +##### 所有路径都报404错误 + +出现这种情况的原因是`shiro-spring-boot-starter`在进行自动配置的时候,没有正确的配置`DefaultAdvisorAutoProxyCreator`的Bean,所以只需要在Shiro的配置类中加入这个Bean即可,代码可以仿照以下Bean实例化代码添加。 + +```java +@Bean +public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { + var creator = new DefaultAdvisorAutoProxyCreator(); + creator.setUsePrefix(true); + + return creator; +} +``` + ### 处理用户登录 用户登录主要是一个收集用户提供的认证信息并进行校验的过程,在Shiro提供的Realm加持下,处理用户登录的过程被简化成了只需要组装`AuthenticationToken`,不必再次实现认证信息的校验的过程。以下是一个登录Controller的示例。