shrio&springboot&layui 前后端分离思路,及实现核心业务逻辑
总结:上期我们说到表设计,这次来实现。
前后端分离思路:登入返回 token 根据token 获取菜单数据,动态渲染 路由。重新shiro 的处理逻辑就行,如没有权限,未认证 登 返货json 格式。(前端控制 路由跳转,后端负责数据)
不分离 就常规套路,登入后 获取菜单信息 渲染页面,未认证直接跳转 登入页,或者没有权限页面,也可以通过标签 直接看不到,反正就是简单的很。(后端负责所有页面跳转和数据扭转)。
核心代码如下:
public class CustomRealm extends AuthorizingRealm { @Autowired private SysUsersService sysUsersService; @Autowired private SysUsersMapper usersMapper; /** * 授权 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { if (principals == null) { throw new AuthorizationException("请重新登入"); } // principals 就是 认证 传的 第一个 参数 传什么 强转什么 SysUsers user = (SysUsers) getAvailablePrincipal(principals); List<SysPermissions> list = usersMapper.getPermissions(user.getId()); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); Set<String> permissions = list.stream().map(e->e.getPermission()).collect(Collectors.toSet()); info.setStringPermissions(permissions); return info; } /** * 认证 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken fromToken = (UsernamePasswordToken) token; String username = fromToken.getUsername(); QueryWrapper<SysUsers> queryWrapper = new QueryWrapper(); List<SysUsers> usersList = sysUsersService.list(queryWrapper); if(CollectionUtils.isEmpty(usersList)){ throw new UnknownAccountException("用户不存在"); } SysUsers user = usersList.get(0); // 一般重次 登入 3次 密码错误 就锁定 需要重下 shiro 登入失败 逻辑 if(user.getLocked()>0){ throw new LockedAccountException("账户被锁定,请联系管理员"); } //从数据 查询 根据用户username 用户 然后 交给 shiro 去匹配 密码 用户名 相等 SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName()); /* if (user.getSalt() != null) { info.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt())); }*/ return info; } }
核心配置如下:
@Configuration public class ShiroConfig { /** * 注入自定义的realm,告诉shiro如何获取用户信息来做登录或权限控制 */ @Bean public Realm realm() { return new CustomRealm(); } /** * 这里统一做鉴权,即判断哪些请求路径需要用户登录,哪些请求路径不需要用户登录。 * 这里只做鉴权,不做权限控制,因为权限用注解来做。 * @return */ @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); Map<String, String> chainDefinition = new LinkedHashMap<>(); chainDefinition.put("/user/index", "anon"); chainDefinition.put("/user/login", "anon"); chainDefinition.put("/static/**", "anon"); chainDefinition.put("/css/**", "anon"); chainDefinition.put("/js/**", "anon"); chainDefinition.put("/swagger-ui.html", "anon"); chainDefinition.put("/doc.html", "anon"); chainDefinition.put("/swagger-resources", "anon"); chainDefinition.put("/swagger-resources/configuration/security", "anon"); chainDefinition.put("/swagger-resources/configuration/ui", "anon"); chainDefinition.put("/v2/api-docs", "anon"); chainDefinition.put("/webjars/springfox-swagger-ui/**", "anon"); // 1、创建过滤器Map,用来装自定义过滤器 LinkedHashMap<String, Filter> map = new LinkedHashMap<>(); // 2、将自定义过滤器放入map中,如果实现了自定义授权过滤器,那就必须在这里注册,否则Shiro不会使用自定义的授权过滤器 map.put("authc", new MyFormAuthenticationFilter()); // 3、将过滤器Ma绑定到shiroFilterFactoryBean上 factoryBean.setFilters(map); //设置 未认证跳转 当然重写 过滤器 也可以 factoryBean.setLoginUrl("/user/unauth"); //设置 未授权跳转 factoryBean.setUnauthorizedUrl("/user/unperms"); //除了以上的请求外,其它请求都需要登录 chainDefinition.put("/**", "authc"); // chainDefinition.put("/**", "anon"); factoryBean.setFilterChainDefinitionMap(chainDefinition); return factoryBean; } @Bean public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); /** * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。 * 在@Controller注解的类的方法中加入@RequiresRole注解,会导致该方法无法映射请求,导致返回404。 * 加入这项配置能解决这个bug */ creator.setProxyTargetClass(true); creator.setUsePrefix(true); return creator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //securityManager.setRealms(Arrays.asList(realm())); securityManager.setRealm(realm()); // securityManager.setCacheManager(cacheManager()); // 设置realm. 可以设置 多ream 和 策略 //securityManager.setAuthenticator(authenticator()); return securityManager; } /* //添加realm @Bean public Authenticator authenticator() { ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator(); //设置两个Realm,一个用于用户登录验证和访问权限获取;一个用于jwt token的认证 authenticator.setRealms(Arrays.asList(jwtRealm())); //设置多个realm认证策略,一个成功即跳过其它的 authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy()); return authenticator; }*/ /** * 使用默认session * * @return */ @Bean(name="sessionManager") public ServletContainerSessionManager servletContainerSessionManager() { ServletContainerSessionManager sessionManager = new ServletContainerSessionManager(); return sessionManager; } /* *//** * 凭证匹配器 * @return *//* @Bean(name = "hashedCredentialsMatcher") public HashedCredentialsMatcher getHashedCredentialsMatcher(){ HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher("SHA-256"); hashedCredentialsMatcher.setHashIterations(2); hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); return hashedCredentialsMatcher; }*/ }
效果如下:
接口文档如下:
就是折磨简单 虽然没啥难度,但是今天还是搞了半天。
代码结构:
源码地址 有兴趣的可以看看
https://github.com/lyc88/shiro
elk