认证与授权

关于授权

  • 一般来说用户通过认证后既可以访问服务获取资源,提交、更新信息,甚至删除信息
  • 但是每个用户存在的环境不同,可以操作的内容也不同,如果所有用户都有权限去新增删除用户那就会乱套了,所以系统中的用户具有角色、部门甚至是岗位的描述。这种描述就是用来对用户做分级,达到不同用户的授权独立
    • 简单的说。比如部门A的用户可以有查询的权限,部门B的用户可以有新增、更新的权限。两个部门用户的权限独立,如果再细分一些,部门B的用户分为insert角色,与update角色
      • insert 角色可以做新增
      • update 角色可以做更新
  • 通过分级授权可以管理和明确权限的分级,保证了业务上的安全性和稳定性
  • 授权一般非为操作权限和数据权限,上面说的是操作权限,数据权限就和业务紧密结合。

关于Shiro

  • Shiro是由Apache基金会开发的开源JAVA权限管理框架,该框架可以很好的Spring整合。
  • Shiro 官网
  • Shiro包含了三个核心的组件
    • 1.Subject 主体 官方文档
      • 在Shiro中Subject不仅仅指人,它指代了所有和软件交互的实体,大部分情况下储存是用户的信息
    • 2.SecurityManager 安全管理器 官方文档
      • 就如同名字一样,Shiro通过SecurityManager管理者所有的内部组件实例,并通过它提供各种服务。是一个典型的Facade模式,默认实现是DefaultSecurityManager
      • 输入图片说明
    • 3.Realms 安全域 官方文档
      • Realm 本质上一个安全相关的DAO,它封装了数据源的连接细节,并在需要时,将合适的信息提供给Shiro使用,可以存在多个Realm,但是最少需要一个
      • Shiro 提供了几种基础的Realm
      • 输入图片说明
      • 一般情况下会自定义Realm,因为需要整合实际的认证逻辑。通过继承AuthorizingRealm 重写两个方法即可,分别是
        • 认证 doGetAuthenticationInfo(AuthenticationToken token)
        • 授权 doGetAuthorizationInfo(PrincipalCollection principals)
        • 输入图片说明
        • 然后交由SecurityManager统一管理
  • Shiro 支持权限URL权限过滤通过不同的Filter来做处理
    • anon 匿名 /user/**=anon 没有任何权限过滤 AnonymousFilter
    • authc 认证 /user/id=authc 必须通过认证才可以访问使用 AuthenticationFilter
    • roles 角色 /user/**=roles[admin,...] 可以指定多个参数,通过逗号分割 AuthorizationFilter
    • perms 权限 /user/update/id=perms[user:update:*],...可以指定多个参数,通过逗号分割 AuthorizationFilter
    • authcBasic 基础认证 /user/**=authcBasic AuthenticationFilter
    • ssl 安全请求 /user/**=ssl 协议必须为https AuthorizationFilter
    • 输入图片说明

如何组合Shiro与Jwt

  • authc 认证的默认实现有两种 输入图片说明
    • 第一种是基础的Http认证
    • 第二种是用户密码的认证方式
  • Shiro管理权限与认证,Jwt负责作为令牌记录信息,这种模式显然原生的Shiro是无法支持的。所以需要对Shiro的authc的filter做自定义。
    • 需要注意的是对应的每个权限认证都是独立的filter完成,比如定义了/xxx/**=anon,自定的filter继承了AuthenticatingFilter,这是不会进入自定的filter,只会进入AnonymousFilter的子类。因为在PathMatchingFilter中对路径进行了匹配,调用对应的filter
    • 输入图片说明
  • 整合Shrio+Jwt 需要自定义filter 与 Realm
    • 自定义filter 来判断哪些哪些请求
    • 自定已Realm 进行具体的认证与授权
    /**
     * 创建token
     *
     * @param request  请求
     * @param response 响应
     * @return {@link ThunderToken}
     */
    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {
        HttpServletRequest servletRequest = (HttpServletRequest) request;
        return new ThunderToken(servletRequest.getHeader(CommonConstants.AUTHORIZATION_SIGN));
    }

    /**
     * 做令牌拦截
     * 判断请求头是否包含令牌 
     *
     * @param request     请求
     * @param response    响应
     * @param mappedValue mappedValue
     * @return 通过/拒绝
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        // 生成令牌
        ThunderToken token = (ThunderToken) createToken(request, response);
        boolean isAccessAllowed = false;
        // 判断令牌是否存在
        if (token.getToken() != null && !token.getToken().isEmpty()) {
            try {
                // 如果存在交由Realm认证
                getSubject(request, response).login(token);
                // 没有异常表示通过认证,如果Realm有异常向上抛出由Filter统一处理,反馈友好的认证信息
                isAccessAllowed = true;
            } catch (TokenExpiredException e) {
                expiredToken(response);
            } catch (IllegalTokenException e) {
                illegalToken(response);
            } catch (Exception e) {
                unknownError(response);
            }
        } else {
            unAuthorized(response);
        }
        return isAccessAllowed;
    }       
/**
 * 系统令牌
 * 因为前后端分离,所以只需要记录TOKEN密钥即可
 */
public class ThunderToken implements AuthenticationToken {

    private String token;

    public ThunderToken(String token) {
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    @Override
    public String toString() {
        return "ThunderToken{" +
                "token='" + token + '\'' +
                '}';
    }
}

各种问题的解决办法

  • 请求不进入filter
    • 确认配置的是否是 authc 如果不是不会进入filter
  • 服务之间调用令牌不会传递
    • 如果使用的Feign需要自定义拦截器,获取到RequestTemplate对象自定义请求头
    • 输入图片说明
    • 输入图片说明

转载于:https://my.oschina.net/shadowli/blog/3046007

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值