Spring boot整合shiro+jwt实现前后端分离
本文实例为大家分享了Springboot整合shiro+jwt实现前后端分离的具体代码,供大家参考,具体内容如下
这里内容很少很多都为贴的代码,具体内容我经过了看源码和帖子加了注释。帖子就没用太多的内容
先下载shiro和jwt的jar包
org.apache.shiro shiro-spring 1.4.0 org.apache.shiro shiro-ehcache 1.4.0 com.auth0 java-jwt 3.4.0 io.jsonwebtoken jjwt 0.9.0
创建shiro的自定义的Realm
代码如下:
packagecom.serverprovider.config.shiro.userRealm; importcom.spring.common.auto.autoUser.AutoUserModel; importcom.spring.common.auto.autoUser.extend.AutoModelExtend; importcom.serverprovider.config.shiro.jwt.JWTCredentialsMatcher; importcom.serverprovider.config.shiro.jwt.JwtToken; importcom.serverprovider.service.loginService.LoginServiceImpl; importcom.util.Redis.RedisUtil; importcom.util.ReturnUtil.SecretKey; importcom.util.encryption.JWTDecodeUtil; importio.jsonwebtoken.Claims; importorg.apache.log4j.Logger; importorg.apache.shiro.authc.*; importorg.apache.shiro.authz.AuthorizationInfo; importorg.apache.shiro.authz.SimpleAuthorizationInfo; importorg.apache.shiro.realm.AuthorizingRealm; importorg.apache.shiro.subject.PrincipalCollection; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.web.bind.annotation.ExceptionHandler; importjava.util.HashSet; importjava.util.List; importjava.util.Set; publicclassUserRealmextendsAuthorizingRealm{ privateLoggerlogger=Logger.getLogger(UserRealm.class); @AutowiredprivateLoginServiceImplloginService; publicUserRealm(){ //这里使用我们自定义的Matcher验证接口 this.setCredentialsMatcher(newJWTCredentialsMatcher()); } /** *必须重写此方法,不然Shiro会报错 */ @Override publicbooleansupports(AuthenticationTokentoken){ returntokeninstanceofJwtToken; } /** *shiro身份验证 *@paramtoken *@returnboolean *@throwsAuthenticationException抛出的异常将有统一的异常处理返回给前端 * */ @Override protectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationTokentoken)throwsAuthenticationException{ /** *AuthenticationToken *JwtToken重写了AuthenticationToken接口并创建了一个接口token的变量 *因为在filter我们将token存入了JwtToken的token变量中 *所以这里直接getToken()就可以获取前端传递的token值 */ StringJWTtoken=((JwtToken)token).getToken(); /** *Claims对象它最终是一个JSON格式的对象,任何值都可以添加到其中 *token解密转换成Claims对象 */ Claimsclaims=JWTDecodeUtil.parseJWT(JWTtoken,SecretKey.JWTKey); /** *根据JwtUtil加密方法加入的参数获取数据 *查询数据库获得对象 *如为空:抛出异常 *如验证失败抛出AuthorizationException */ Stringusername=claims.getSubject(); Stringpassword=(String)claims.get("password"); AutoModelExtendprincipal=loginService.selectLoginModel(username,password); returnnewSimpleAuthenticationInfo(principal,JWTtoken,"userRealm"); } @Override protectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollectionprincipals){ SimpleAuthorizationInfoinfo=null; /** *PrincipalCollection对象 *文档里面描述:返回从指定的Realm仅作为Collection返回的单个Subject的对象,如果没有来自该领域的任何对象,则返回空的Collection。 *在登录接口放入权限注解返回的错误信息:Subject.login(AuthenticationToken)或SecurityManager启用'RememberMe'功能后成功自动获取这些标识主体 *当调用Subject.login()方法成功后PrincipalCollection会自动获得该对象如没有认证过或认证失败则返回空的Collection并抛出异常 *getPrimaryPrincipal():返回在应用程序范围内使用的主要对象,以唯一标识拥有帐户。 */ Objectprincipal=principals.getPrimaryPrincipal(); /** *得到身份对象 *查询该用户的权限信息 */ AutoUserModeluser=(AutoUserModel)principal; ListroleModels=loginService.selectRoleDetails(user.getId()); try{ /** *创建一个Set,来放置用户拥有的权限 *创建SimpleAuthorizationInfo,并将办好权限列表的Set放入. */ Set rolesSet=newHashSet(); for(Stringrole:roleModels){ rolesSet.add(role); } info=newSimpleAuthorizationInfo(); info.setStringPermissions(rolesSet);//放入权限信息 }catch(Exceptione){ thrownewAuthenticationException("授权失败!"); } returninfo; } }
这个授权方法遇到的坑比较少,就是在最终验证的时候网上很照抄过来的帖子一点都没有验证就粘贴赋值,在这里严重吐槽。
在使用jwt最为token而取消shiro传统的session时候,我们的需要重写shiro的验证接口 CredentialsMatcher,在自定义的realm
中我们加入我们重写的验证方法,在调用SimpleAuthenticationInfo()方法进行验证的时候,shiro就会使用重写的验证接口。
此处为大坑。
贴上代码如下:
importcom.spring.common.auto.autoUser.extend.AutoModelExtend; importorg.apache.log4j.Logger; importorg.apache.shiro.authc.AuthenticationInfo; importorg.apache.shiro.authc.AuthenticationToken; importorg.apache.shiro.authc.credential.CredentialsMatcher; /** *CredentialsMatcher *接口由类实现,该类可以确定是否提供了AuthenticationToken凭证与系统中存储的相应帐户的凭证相匹配。 *Shiro加密匹配重写匹配方法CredentialsMatcher使用JWTUtil匹配方式 */ publicclassJWTCredentialsMatcherimplementsCredentialsMatcher{ privateLoggerlogger=Logger.getLogger(JWTCredentialsMatcher.class); /** *Matcher中直接调用工具包中的verify方法即可 */ @Override publicbooleandoCredentialsMatch(AuthenticationTokenauthenticationToken,AuthenticationInfoauthenticationInfo){ Stringtoken=(String)((JwtToken)authenticationToken).getToken(); AutoModelExtenduser=(AutoModelExtend)authenticationInfo.getPrincipals().getPrimaryPrincipal(); //得到DefaultJwtParser Booleanverify=JwtUtil.isVerify(token,user); logger.info("JWT密码效验结果="+verify); returnverify; } }
shiro的配置项 ShiroConfiguration代码如下:
importcom.serverprovider.config.shiro.shiroSysFile.JwtFilter; importcom.serverprovider.config.shiro.userRealm.UserRealm; importorg.apache.log4j.Logger; importorg.apache.shiro.mgt.DefaultSessionStorageEvaluator; importorg.apache.shiro.mgt.DefaultSubjectDAO; importorg.apache.shiro.mgt.SecurityManager; importorg.apache.shiro.mgt.SessionStorageEvaluator; importorg.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; importorg.apache.shiro.spring.web.ShiroFilterFactoryBean; importorg.apache.shiro.web.mgt.DefaultWebSecurityManager; importorg.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator; importorg.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; importorg.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; importjavax.servlet.Filter; importjava.util.*; @Configuration publicclassShiroConfiguration{ privateLoggerlogger=Logger.getLogger(ShiroConfiguration.class); @Bean("shiroFilter") publicShiroFilterFactoryBeanshiroFilter(SecurityManagersecurityManager){ ShiroFilterFactoryBeanshiroFilterFactoryBean=newShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //拦截器 MapfilterChainDefinitionMap=newLinkedHashMap (); //配置不会被拦截的链接顺序判断 filterChainDefinitionMap.put("/login/**","anon"); //添加自己的过滤器并且取名为jwt Map filterMap=newHashMap (); filterMap.put("jwt",newJwtFilter()); shiroFilterFactoryBean.setFilters(filterMap); // 热门推荐