今天在做springboot和shiro整合练习的时候出现了一个错误,希望对初学者遇到同样问题有所帮助
1、错误信息
2019-10-29 11:39:06.809 WARN 14068 --- [nio-8081-exec-1] o.a.shiro.authc.AbstractAuthenticator : Authentication failed for token submission [org.apache.shiro.authc.UsernamePasswordToken - admin, rememberMe=false]. Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException). java.lang.NullPointerException: null at com.example.shiro.config.CustomRealm.doGetAuthenticationInfo(CustomRealm.java:60) ~[classes/:na] at org.apache.shiro.realm.AuthenticatingRealm.getAuthenticationInfo(AuthenticatingRealm.java:571) ~[shiro-core-1.4.1.jar:1.4.1] at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doSingleRealmAuthentication(ModularRealmAuthenticator.java:180) ~[shiro-core-1.4.1.jar:1.4.1] at org.apache.shiro.authc.pam.ModularRealmAuthenticator.doAuthenticate(ModularRealmAuthenticator.java:267) ~[shiro-core-1.4.1.jar:1.4.1] at org.apache.shiro.authc.AbstractAuthenticator.authenticate(AbstractAuthenticator.java:198) ~[shiro-core-1.4.1.jar:1.4.1] at org.apache.shiro.mgt.AuthenticatingSecurityManager.authenticate(AuthenticatingSecurityManager.java:106) [shiro-core-1.4.1.jar:1.4.1] at org.apache.shiro.mgt.DefaultSecurityManager.login(DefaultSecurityManager.java:275) [shiro-core-1.4.1.jar:1.4.1] at org.apache.shiro.subject.support.DelegatingSubject.login(DelegatingSubject.java:260) [shiro-core-1.4.1.jar:1.4.1] at com.example.shiro.controller.UserController.login(UserController.java:32) [classes/:na] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_211] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_211] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_211] at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_211] at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) [spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE] at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) [spring-web-5.2.0.RELEASE.jar:5.2.0.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) [spring-webmvc-5.2.0.RELEASE.jar:5.2.0.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:888) [spring-webmvc-5.2.0.RELEASE.jar:5.2.0.RELEASE] at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) [spring-webmvc-5.2.0.RELEASE.jar:5.2.0.RELEASE] at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) [spring-webmvc-5.2.0.RELEASE.jar:5.2.0.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) [spring-webmvc-5.2.0.RELEASE.jar:5.2.0.RELEASE] at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) [spring-webmvc-5.2.0.RELEASE.jar:5.2.0.RELEASE] at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) [spring-webmvc-5.2.0.RELEASE.jar:5.2.0.RELEASE] at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909) [spring-webmvc-5.2.0.RELEASE.jar:5.2.0.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) [tomcat-embed-core-9.0.27.jar:9.0.27] at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) [spring-webmvc-5.2.0.RELEASE.jar:5.2.0.RELEASE] at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) [tomcat-embed-core-9.0.27.jar:9.0.27]
上面出现的NPE , at com.example.shiro.config.CustomRealm.doGetAuthenticationInfo(CustomRealm.java:60) ~[classes/:na] 是String password = userService.getPasswordByUsername(userName); 这行代码
2、realm源代码
public class CustomRealm extends AuthorizingRealm { @Autowired private RoleService roleService; @Autowired private RolePermissionService rolePermissionService; @Autowired private UserService userService; //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //从主体传过来的认证信息中,获取用户名 String userName = (String)authenticationToken.getPrincipal(); //通过用户名去到数据库中获取凭证 String password = userService.getPasswordByUsername(userName); if (StringUtils.isEmpty(password)) { return null; } SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, password, "costomRealm"); //设置盐salt simpleAuthenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("pyy")); return simpleAuthenticationInfo; } }
3、shiro配置类代码
@Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); CustomRealm customRealm = new CustomRealm(); //加密配置 HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); //设置加密算法名称 matcher.setHashAlgorithmName("md5"); //设置加密次数 matcher.setHashIterations(1); customRealm.setCredentialsMatcher(matcher); //设置Realm securityManager.setRealm(customRealm); return securityManager; }
4、debug时的现象
报NPE, 说明userService没有注入进来,为什么?
因为shiro 的realm本身相当于过滤器,而在登录访问时会先走过滤器,再执行service注入,这就会出现还没有进行service注入,已经再realm中调用注入的对象,所以就会出现上述现象
解决办法:
将realm已bean的形式生成,那么再生成时就会强制注入service对象,这样就避免了上述问题,具体代码如下,
@Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置Realm securityManager.setRealm(customRealm()); return securityManager; } @Bean public CustomRealm customRealm() { CustomRealm customRealm = new CustomRealm(); //加密配置 HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); //设置加密算法名称 matcher.setHashAlgorithmName("md5"); //设置加密次数 matcher.setHashIterations(1); customRealm.setCredentialsMatcher(matcher); return customRealm; }