栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

shiro

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

shiro

1、什么是权限管理

权限管理包括用户身份认证和授权两部分,简称认证授权。

身份认证:就是判断一个用户是否为合法用户的处理过程。最简单的就是通过核对用户名和密码,看是否与系统中存储的该用户的用户名和密码一致,来判断身份是否正确

授权:即访问控制,控制谁能访问哪些资源。主体进行身份认证后需要分配权限可访问的资源。

2、整体架构

SecurityManager 外部应用与subject进行交互,subject记录当前操作对象,subject在shiro中是个接口,接口中定义了很多认证授权相关的方法,外部程序通过subject进行认证授权,而subject是通过SecurityManager安全管理器进行认证授权

Authenticator 做认证

Authorizer 做授权

Session Manager 存储用户session

Session DAO 对用户Session的操作

Cache Manager 缓存用户认证和授权的信息

Pluggable Realms 获取认证和授权信息并判断,可以操作JDBC,NoSql数据库等等,说白了认证和授权器都用的他

3、shiro中认证的关键对象

subject:主体 其实就是登录的用户或程序

principal:身份信息 大多数情况下是用户名,是主体进行身份认证的标识,标识必须唯一

credential:凭证信息 是只有主体自己知道的安全信息,如密码,证书等。通常情况下是密码。

4、认证授权

主体(登录用户)登录网站,输入用户名和密码,对应shiro中的身份信息principal和凭证creditial ,将这两个玩意封装为token(令牌),shiro再进行认证(认证器和授权器调用Realms来判断信息是否符合)
流程图
认证授权

用户登录大致流程

权限五张表关系图

5、小demo
public static void main(String[] args) {
        //创建安全管理器
        DefaultSecurityManager securityManager = new DefaultSecurityManager();

        //给安全管理器设置Realms  做数据调配
        securityManager.setRealm(new IniRealm("classpath:shiro.ini"));

        //securityUtils工具类  给全局安全工具类设置安全管理器
        SecurityUtils.setSecurityManager(securityManager);

        //subject主体
        Subject subject = SecurityUtils.getSubject();

        //创建令牌
        UsernamePasswordToken token = new UsernamePasswordToken("xiaocheng","123");

        //认证
        try{
            System.out.println("认证状态"+subject.isAuthenticated());
            subject.login(token);
            System.out.println("认证状态"+subject.isAuthenticated());

        }catch(UnknownAccountException e){
            System.out.println("账号不正确");
        }catch(IncorrectCredentialsException e ){
            System.out.println("密码不正确");
        }
}
6、源码分析
DelegatingSubject类
public void login(AuthenticationToken token) throws AuthenticationException {
        this.clearRunAsIdentitiesInternal();
        Subject subject = this.securityManager.login(this, token);  //往里进执行认证操作
        String host = null;
        PrincipalCollection principals;
        if (subject instanceof DelegatingSubject) { //执行到这里,表示认证成功,并返回一个代表登录用户的subject
            DelegatingSubject delegating = (DelegatingSubject)subject;
            principals = delegating.principals;
            host = delegating.host;
        } else {
            principals = subject.getPrincipals();
        }

        if (principals != null && !principals.isEmpty()) {
            this.principals = principals;  //认证成功,拿到用户名
            this.authenticated = true;  //设置认证字段为true
            if (token instanceof HostAuthenticationToken) {
                host = ((HostAuthenticationToken)token).getHost();
            }

            if (host != null) {
                this.host = host;
            }

            Session session = subject.getSession(false);  //拿到subject主体的session对象
            if (session != null) {
                this.session = this.decorate(session);    //将认证信息保存至session中
            } else {
                this.session = null;
            }

        } else {
            String msg = "Principals returned from securityManager.login( token ) returned a null or empty value.  This value must be non null and populated with one or more elements.";
            throw new IllegalStateException(msg);
        }
    }



DefaultSecutiryManager类
public Subject login(Subject subject, AuthenticationToken token) throws AuthenticationException {
        AuthenticationInfo info;
        try {
            info = this.authenticate(token);   //往里进执行认证操作
        } catch (AuthenticationException var7) {
            AuthenticationException ae = var7;

            try {
                this.onFailedLogin(token, ae, subject);
            } catch (Exception var6) {
                if (log.isInfoEnabled()) {
                    log.info("onFailedLogin method threw an exception.  Logging and propagating original AuthenticationException.", var6);
                }
            }

            throw var7;
        }

        Subject loggedIn = this.createSubject(token, info, subject);//回溯回来后,新建subject对象,用于表示登录用户
        this.onSuccessfulLogin(token, info, loggedIn);
        return loggedIn;  //返回subject主体,继续回溯
    }



AuthenticatingSecutityManager类
public AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
        return this.authenticator.authenticate(token);  //经过回溯回来的用户名被封装为AuthenticationInfo类
    }



AbstractAuthenticator类
public final AuthenticationInfo authenticate(AuthenticationToken token) throws AuthenticationException {
        if (token == null) {
            throw new IllegalArgumentException("Method argument (authentication token) cannot be null.");
        } else {
            log.trace("Authentication attempt received for token [{}]", token);

            AuthenticationInfo info;
            try {
                info = this.doAuthenticate(token);  //往里进执行认证操作
                if (info == null) {
                    String msg = "No account information found for authentication token [" + token + "] by this Authenticator instance.  Please check that it is configured correctly.";
                    throw new AuthenticationException(msg);
                }
            } catch (Throwable var8) {
                AuthenticationException ae = null;
                if (var8 instanceof AuthenticationException) {
                    ae = (AuthenticationException)var8;
                }

                if (ae == null) {
                    String msg = "Authentication failed for token submission [" + token + "].  Possible unexpected error? (Typical or expected login exceptions should extend from AuthenticationException).";
                    ae = new AuthenticationException(msg, var8);
                    if (log.isWarnEnabled()) {
                        log.warn(msg, var8);
                    }
                }

                try {
                    this.notifyFailure(token, ae);
                } catch (Throwable var7) {
                    if (log.isWarnEnabled()) {
                        String msg = "Unable to send notification for failed authentication attempt - listener error?.  Please check your AuthenticationListener implementation(s).  Logging sending exception and propagating original AuthenticationException instead...";
                        log.warn(msg, var7);
                    }
                }

                throw ae;
            }

            log.debug("Authentication successful for token [{}].  Returned account [{}]", token, info);
            this.notifySuccess(token, info);  //用户名和密码都认证成功,设置token成功标志
            return info;  //继续返回用户名
        }
    }



ModularRealmAuthenticator类
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        this.assertRealmsConfigured();
        Collection realms = this.getRealms();
        return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);  //往里进执行认证操作
    }
    
    
protected AuthenticationInfo doSingleRealmAuthentication(Realm realm, AuthenticationToken token) {
        if (!realm.supports(token)) {
            String msg = "Realm [" + realm + "] does not support authentication token [" + token + "].  Please ensure that the appropriate Realm implementation is configured correctly or that the realm accepts AuthenticationTokens of this type.";
            throw new UnsupportedTokenException(msg);
        } else {
            AuthenticationInfo info = realm.getAuthenticationInfo(token);  //往里进执行认证操作
            if (info == null) {
                String msg = "Realm [" + realm + "] was unable to find account data for the submitted AuthenticationToken [" + token + "].";
                throw new UnknownAccountException(msg);
            } else {
                return info;  //返回认证成功的用户名 ,继续回溯
            }
        }
    }



AuthenticatingRealm类
public final AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        AuthenticationInfo info = this.getCachedAuthenticationInfo(token);
        if (info == null) {
            info = this.doGetAuthenticationInfo(token);  //往里进执行认证操作
            log.debug("Looked up AuthenticationInfo [{}] from doGetAuthenticationInfo", info);
            if (token != null && info != null) {  
                this.cacheAuthenticationInfoIfPossible(token, info);  //将用户名缓存,往下执行判断密码凭证
            }
        } else {
            log.debug("Using cached authentication info [{}] to perform credentials matching.", info);
        }

        if (info != null) {  //info为用户名认证成功返回的用户名
            this.assertCredentialsMatch(token, info);  //认证密码凭证,里边用的是equals,大致是两个数组元素分别比较
        } else {
            log.debug("No AuthenticationInfo found for submitted AuthenticationToken [{}].  Returning null.", token);
        }

        return info;  //到此,密码和用户名都认证成功了,返回用户名,继续回溯
    }



SimpleAccountRealm类
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken)token;  
        SimpleAccount account = this.getUser(upToken.getUsername());  //往里进执行用户名认证
        if (account != null) {
            if (account.isLocked()) {  //判断账号是否被锁
                throw new LockedAccountException("Account [" + account + "] is locked.");
            }

            if (account.isCredentialsExpired()) {  //判断账号的密码是否过期
                String msg = "The credentials for account [" + account + "] are expired";
                throw new ExpiredCredentialsException(msg);
            }
        }

        return account;  //回溯上去,到此用户名认证成功
    }

总结:

1、首先进行的是用户名principal的认证,认证成功后,再进行密码credential的认证,都认证成功了,才生成登录用户对应得subject对象存储在session中

2、进行用户名认证是在SimpleAccountRealm类中的doGetAuthenticationInfo方法中。进行密码凭证认证的是在

AuthenticatingRealm类中的getAuthenticationInfo方法中

3、根据继承关系,Realm接口–》CacheingRealm实现类–》AuthenticatingRealm实现类–》AuthorizingRealm实现类–》SimpleAccountRealm实现类,认证用户名的方法doGetAuthenticationInfo是定义在AuthenticatingRealm类中,所以,我们可以通过继承AuthenticatingRealm或AuthorizingRealm来重写doGetAuthenticationInfo方法,而密码凭证认证是直接在AuthenticatingRealm类中实现的。其实,shiro将密码由它自己管理也是有道理的:密码很重要,不应该由程序员来操作,同时,涉及到加盐加密时,shiro可以自己操作。

4、我们可以继承AuthenticatingRealm或AuthorizingRealm类,重写doGetAuthenticationInfo方法来自定义认证用户名,在里面调用数据库啊,redis啥的,自定义认证。

5、对了,CacheingRealm类是用来做缓存的,AuthenticatingRealm类是用来做认证的,AuthorizingRealm是用来做授权的,所以,如果你既想自定义认证,又想自定义授权,那就只能继承AuthorizingRealm类

6、SimpleAuthorizationInfo类用来存储subject主体的授权信息,SimpleAuthenticationInfo类用来存储subject主体的认证信息

7、自定义realm
public class CustomerRealm extends AuthorizingRealm {
    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //从token中获取用户名
        String principle  = (String)authenticationToken.getPrincipal();
        System.out.println("在自定义中得到的用户名为:"+principle);
        //根据身份信息去MySQL或redis查询判断
        if(principle.equals("xiaocheng")){
            return new SimpleAuthenticationInfo("xiaocheng","123","CustomerRealm");
        }
        return null;
    }

    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }
}

public class testCustomerRealm {
    public static void main(String[] args) {
        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        //设置自定义realm
        defaultSecurityManager.setRealm(new CustomerRealm());

        //将安全工具类设置安全工具类
        SecurityUtils.setSecurityManager(defaultSecurityManager);

        //通过安全工具类获取subject
        Subject subject = SecurityUtils.getSubject();

        //创建token
        UsernamePasswordToken token = new UsernamePasswordToken("xiaocheng","123");

        //认证
        try{
            subject.login(token);
            System.out.println(subject.isPermitted());
        }catch(Exception e){
            e.printStackTrace();
        }

    }
}
8、盐

加盐加密,密文存储

MD5介绍

作用:一般用来加密或签名

特点:MD5是非对称加密算法,不可逆,只能由明文转换为密文,无论相同内容执行多少次md5生成的结果始终是一致的(可以理解为幂等性操作)

生成结果:始终是一个16进制的32位长度字符串

对于加盐加密的讨论:

首先,加盐是必须的,如果不加盐,用户注册的账号和密码在数据库中以明文存储,如果被黑,就完蛋。

通过随机生成的盐结合账号,再使用MD5进行加密后得到的密码在数据库中存储,则是密文形式了。

这时有个问题啊,如果用户登录呢?怎么判断用户登录的账号密码和数据库中查到的账号密码是一致的呢?当初随机生成的盐我们不知道啊。

所以,为了解决这个问题,需要在设计数据库时,需设计盐字段来保存随机生成的盐。

这时候,可能有人又会问:我知道数据库中的账号密码和盐了,那不是还可以拿到账号密码吗?

注意,shiro只是尽可能的保证数据安全,不是百分百,而且,这种方式下,数据库中存储的是密文密码和盐,黑客还是比较难攻克的。

demo,用户登录使用哈希加盐加散列和数据库比较

public class testCustomerMD5 {
    public static void main(String[] args) {
        //创建安全管理器
        DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();

        //注入realm
        CustomerMD5Realm realm = new CustomerMD5Realm();

        //设置realm使用hash凭证匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //使用的算法
        credentialsMatcher.setHashAlgorithmName("md5");
        //散列的次数
        credentialsMatcher.setHashIterations(23);
        realm.setCredentialsMatcher(credentialsMatcher);


        //设置自定义realm
        defaultSecurityManager.setRealm(realm);



        //将安全工具类设置安全工具类
        SecurityUtils.setSecurityManager(defaultSecurityManager);

        //通过安全工具类获取subject
        Subject subject = SecurityUtils.getSubject();

        //创建token
        UsernamePasswordToken token = new UsernamePasswordToken("xiaocheng","123");

        //认证
        try{
            subject.login(token);
            System.out.println(subject.isAuthenticated());
            System.out.println("认证成功");
        }catch(UnknownAccountException e){
            System.out.println("账号错误");
        }catch(IncorrectCredentialsException e ){
            System.out.println("密码错误");
        }
    }
    
public class CustomerMD5Realm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String principal = (String)authenticationToken.getPrincipal();
        if(principal.equals("xiaocheng")){
            return new SimpleAuthenticationInfo("xiaocheng","ca62283396fccb5eae5f78f9f1fce6eb", ByteSource.Util.bytes("x*0y"),"CustomerMd5Realm");
        }
        return null;
    }
}

总结:shiro默认使用的是CredentialsMatcher,凭证匹配器,这完意默认使用的是equals方法。所以,如果我们想要登录时将账号密码加盐加密和数据库中查找到的比较,需要使用这玩意的子类HashedCredentialsMatcher

然后主要操作的就是CredentialsMatcher。

9、授权

授权可以理解为who对what进行how操作

who可以理解为登录的用户(主体subject),what理解为资源(可以是页面,按钮等等),how理解为权限(增删改查)

认证授权口述:

用户通过网页登录,携带身份信息(用户名)和凭证信息(密码),进入后台,shiro将其转为token进行认证,如果合法,继续进行授权

授权方式:

基于角色的访问控制

if(subject.hasRole("admin")){
    //满足则继续
}

基于资源的访问控制

if(subject.isPermission("user:*create"))

权限字符串

规则是:资源标识符:操作:资源实例标识符,意思是对哪个资源的哪个实例具有什么操作

例如:

用户创建权限:user:create或user:create:*

用户修改实例001的权限:user:update:001

shiro中授权编程实现方式

编程式

Subject subject = SecurityUtils.getSubject();
if(subject.hasRole("admin")){
    //有权限
}else {
    //无权限
}

注解式

@RequiresRoles("admin")
public void hello(){
    //有权限
}

标签式

JSP/GSP 标签:在JSP/GSP页面通过相应的标签完成



注意:Thymeleaf 中使用shiro需要额外集成

总结:shiro的授权可以通过前端或后端来设置

10、整合spring boot

整体思路:前端请求被shiro框架中的shiroFilter拦截,shiroFilter会调用securityManager管理器进行认证,安全管理器势必要调用数据进行校验,继而用到了Realm操作数据库或redis等拿取数据,认证完成后如果是受限资源,还需要进行授权操作,如果是公共资源,则直接可以使用。

shiroFilter–>SecurityManager–>Realm–>credentialsMatcher(凭证匹配器,算法,hash次数)

shiro常见的过滤器

配置缩写 对应过滤器 功能
anon AnonymousFilter 指定url可以匿名访问
authc FormAuthenticationFilter 指定url需要form表单登录

11、权限信息的缓存

用户的权限信息基本上不会有太大的变化,我们不可能每次用户登录都要查询数据库进行认证授权操作,频繁的访问数据库会导致数据库压力大。

所以,我们一般会给权限信息加缓存

1、使用CacheManager

基本流程:用户登录–》shiro拦截–》如果此用户的授权信息已在缓存管理器

CacheManager中,则直接读取,如果没有,则去数据库中查询然后放入缓存,待下次授权查询时,直接找缓存

shiro默认使用EhCache实现缓存(了解即可)

使用步骤:引入依赖–》Realm开启缓存管理

前面说过继承关系:Realm接口–》CacheingRealm实现类–》AuthenticatingRealm实现类–》AuthorizingRealm实现类–》SimpleAccountRealm实现类

在CacheingRealm中的CacheManager管理器,我们可以修改它。实现自定义缓存管理。

这种缓存是本地缓存,当本地服务宕机,则缓存消失。我们需要做成分布式缓存,这样,即使某个应用程序死机,其他应用程序任然可以使用缓存,这涉及到redis。

2、redis作为缓存

public class RedisCacheManager implements CacheManager{
    
}public  Cache getCache(String name) throws CacheException {
        this.logger.debug("get cache, name=" + name);
        Cache cache = (Cache)this.caches.get(name);
        if (cache == null) {
            cache = new RedisCache(this.redisManager, this.keySerializer, this.valueSerializer, this.keyPrefix + name + ":", this.expire, this.principalIdFieldName);
            this.caches.put(name, cache);
        }

        return (Cache)cache;
    }

相当于 Map>

12、jeecgboot框架中对shiro的实现
@Slf4j
public class JwtFilter extends BasicHttpAuthenticationFilter {

    private boolean allowOrigin = true;  //表示允许token跨域

    public JwtFilter(){}
    public JwtFilter(boolean allowOrigin){
        this.allowOrigin = allowOrigin;
    }

    
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        try {
            executeLogin(request, response);  //验证token
            return true;
        } catch (Exception e) {
            throw new AuthenticationException("Token失效,请重新登录", e);
        }
    }

    
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        //token存储在请求头,拿到token
        String token = httpServletRequest.getHeader(CommonConstant.X_ACCESS_TOKEN);
        // update-begin--Author:lvdandan Date:20210105 for:JT-355 OA聊天添加token验证,获取token参数
        if(token == null){
            token = httpServletRequest.getParameter("token");
        }
        // update-end--Author:lvdandan Date:20210105 for:JT-355 OA聊天添加token验证,获取token参数
      //把登录中的token转为JwtToken
        JwtToken jwtToken = new JwtToken(token);
        // 提交给realm进行登入,如果错误他会抛出异常并被捕获
        getSubject(request, response).login(jwtToken);
        // 如果没有抛出异常则代表登入成功,返回true
        return true;
    }

    
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if(allowOrigin){
            httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
            httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
            httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
            //update-begin-author:scott date:20200907 for:issues/I1TAAP 前后端分离,shiro过滤器配置引起的跨域问题
            // 是否允许发送cookie,默认cookie不包括在CORS请求之中。设为true时,表示服务器允许cookie包含在请求中。
            httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
            //update-end-author:scott date:20200907 for:issues/I1TAAP 前后端分离,shiro过滤器配置引起的跨域问题
        }
        // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
        if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            httpServletResponse.setStatus(HttpStatus.OK.value());
            return false;
        }
        //update-begin-author:taoyan date:20200708 for:多租户用到
        String tenant_id = httpServletRequest.getHeader(CommonConstant.TENANT_ID);
        TenantContext.setTenant(tenant_id);
        //update-end-author:taoyan date:20200708 for:多租户用到
        return super.preHandle(request, response);
    }
}

ResourceCheckFilter类主要用于路径的拦截,用于权限,表示用户访问某个url是否允许

@Slf4j
public class ResourceCheckFilter extends AccessControlFilter {

    private String errorUrl;

    public String getErrorUrl() {
        return errorUrl;
    }

    public void setErrorUrl(String errorUrl) {
        this.errorUrl = errorUrl;
    }

    
    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
        //通过请求对象获取subject主体
        Subject subject = getSubject(servletRequest, servletResponse);
        String url = getPathWithinApplication(servletRequest);
        log.info("当前用户正在访问的 url => " + url);
        return subject.isPermitted(url);
    }

    
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        log.info("当 isAccessAllowed 返回 false 的时候,才会执行 method onAccessDenied ");

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        response.sendRedirect(request.getContextPath() + this.errorUrl);

        // 返回 false 表示已经处理,例如页面跳转啥的,表示不在走以下的拦截器了(如果还有配置的话)
        return false;
    }

}

JwtToken类,自定义token对象

public class JwtToken implements AuthenticationToken {
	
	private static final long serialVersionUID = 1L;
	private String token;
 
    public JwtToken(String token) {
        this.token = token;
    }
 
    @Override
    public Object getPrincipal() {
        return token;
    }
 
    @Override
    public Object getCredentials() {
        return token;
    }
}

继承的AuthenticationToken,这个类是shiro中的认证信息token。

ShiroConfig类,这个类主要是用于配置shiro,包括:shiroFilter,securityManager、cacheManager(redis实现)

@Slf4j
@Configuration
public class ShiroConfig {

    @Value("${jeecg.shiro.excludeUrls}")
    private String excludeUrls;  //从类路径下的配置文件中读取排除的Url
    @Resource
    LettuceConnectionFactory lettuceConnectionFactory;
    @Autowired
    private Environment env;


    
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        CustomShiroFilterFactoryBean shiroFilterFactoryBean = new CustomShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 拦截器
        Map filterChainDefinitionMap = new linkedHashMap();
        if(oConvertUtils.isNotEmpty(excludeUrls)){
            String[] permissionUrl = excludeUrls.split(",");
            for(String url : permissionUrl){
                filterChainDefinitionMap.put(url,"anon");
            }
        }
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/sys/cas/client/validateLogin", "anon"); //cas验证登录
       

        // 添加自己的过滤器并且取名为jwt
        Map filterMap = new HashMap(1);
        //如果cloudServer为空 则说明是单体 需要加载跨域配置
        Object cloudServer = env.getProperty(CommonConstant.CLOUD_SERVER_KEY);
        filterMap.put("jwt", new JwtFilter(cloudServer==null));
        shiroFilterFactoryBean.setFilters(filterMap);
        // 



我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号