我和我的项目之整合Security

写在前面

开辟了一个专门板块。把项目中遇到过的技术点做了一个整理,形成自己的技术栈。在码云

上也建了一个仓库,存放这些代码。码云地址https://gitee.com/lth1024/SbMuster。欢迎各位大佬们PullRequest~。这期是SpringBoot整合Security框架。Ps:本来是想先学习Shiro的,但手上项目中用的是Security框架,所以先学习Security了后面再介绍Shiro。

推荐学习地址: http://blog.itwolfed.com/blog/14

 

pom

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

起初

只要加入依赖,项目的所有接口都会被自动保护起来。

 

加入上面的jar包后。在地址栏中输入localhost:8080/index,你会发现自动跳转到了一个登陆界面(login.html),我们完全没有写过登陆界面,所以这个是springsecurity自带的一个登录页,登陆的用户名为user,密码是输出在console中的uuid字符串。

在我们配置Security之前,它默认拦截所有页面并会自动生成一个登陆的账号密码,但这显然不是我们想要的样子。下面我们对它进行改造。

简介

Spring Security,这是一种基于Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。

http://blog.itwolfed.com/blog/14链接参考。下面的图很棒。学习学习~

自己在学习搭建过程中把上述步骤进行了简化。

MyAccessDecisionManager的decide()校验

Do it

1.Web服务器启动

可以自定义拦截所需要的页面,也可设置默认放行的页面。项目中配置了Swagger,可以在permitAll()中进行放行。

2.加载过滤链条

3.加载【我的过滤器安全拦截器】

MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter

  • 每种受支持的安全对象类型(方法调用或Web请求)都有自己的拦截器类,它是AbstractSecurityInterceptor的子类。

  • AbstractSecurityInterceptor是一个实现了对受保护对象的访问进行拦截的抽象类。

  • 【我们自定义MyFilterSecurityInterceptor就是想使用我们之前自定义的 AccessDecisionManager和securityMetadataSource。】

@Component
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
//筛选 调用 安全 元数据源
@Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;


//我的访问决策管理器
@Autowired
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
    super.setAccessDecisionManager(myAccessDecisionManager);
}
}
AbstractSecurityInterceptor中的方法说明:
1.beforeInvocation()方法实现了对访问受保护对象的权限校验,内部用到了AccessDecisionManager和AuthenticationManager;
2.finallyInvocation()方法用于实现受保护对象请求完毕后的一些清理工作,主要是如果在beforeInvocation()中改变了SecurityContext,则在finallyInvocation()中需要将其恢复为原来的SecurityContext,该方法的调用应当包含在子类请求受保护资源时的finally语句块中。
3.afterInvocation()方法实现了对返回结果的处理,在注入了AfterInvocationManager的情况下默认会调用其decide()方法。

4.用户登录【AuthenticationManager认证管理器进行响应】

5.通过用户输入的用户名和密码,然后再根据用户定义的密码算法和盐值等进行计算并和数据库比对。

验证成功

MyUserDetailsService进行响应。

6.根据用户名从数据库中提取该用户的权限列表,组合成UserDetails供Spring Security使用。

*

/**
 * @description: GrantedAuthority授予权限
 * 1.GrantedAuthority是通过AuthenticationManager设置到Authentication对象中的
 * 2.所有的Authentication实现类都保存了一个GrantedAuthority列表,其表示用户所具有的权限。
 * 3.然后AccessDecisionManager将从Authentication中获取用户所具有的GrantedAuthority来鉴定用户是否具有访问对应资源的权限。
 */
@Data
public class Role implements GrantedAuthority{
    private Long id;
    private String name;
    /**
     * 权限点可以为任何字符串,不一定非要用角色名。
     */
    @Override
    public String getAuthority() {
        return name;
    }
}
7.用户点击某个功能(URL)触发MyAccessDecisionManager类的decide()

8.通过decide方法对用户的资源访问进行拦截(URL)

那么这个URL就跟MyInvocationSecurityMetadataSourceServiceHashMap 

[权限 对应的所属者]对比。若两者相同,则根据该url提取出Map结构的数据中的value来,这说明,若要请求这个URL,必须具有跟这个URL相对应的权限值(角色)。这个权限有可能是一个单独的权限,也有可能是一个权限列表,也就是说,一个URL有可能被多种权限(角色)访问。

/**
 * 通过传递的参数来决定用户是否有访问对应受保护对象的权限
 * @param authentication 包含了当前的用户信息,包括拥有的权限。这里的权限来源就是前面登录时UserDetailsService中设置的authorities。
 * @param o  就是FilterInvocation对象,可以得到request等web资源
 * @param collection collection是本次访问需要的权限
 *                   增:{角色1,角色2,角色3} 删:{角色1,角色4,角色5} 改:{角色1,角色2}......
 */
@Override
public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
    if(null == collection || collection.size()<=0){
        return;
    }else {
        String needRole;
        Iterator<ConfigAttribute> iterator = collection.iterator();
        while (iterator.hasNext()){
            ConfigAttribute configAttribute = iterator.next();
            needRole = configAttribute.getAttribute();


            /**
             //根据用户id查找用户对应的角色集合
             List<Role> roles = roleMapper.getRolesByUserId(user.getId());
             user.setAuthorities(roles);
             */
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority grantedAuthority : authorities) {
                if(needRole.trim().equals(grantedAuthority.getAuthority())){
                    return;
                }
            }
        }
        throw new AccessDeniedException("当前访问没有权限");
    }
}

9. MyInvocationSecurityMetadataSourceService。用是用来【储存请求与权限的对应关系】


/**
 * 每一个权限对应其所属者的角色 Collection<ConfigAttribute>决策器会用到
 * 增:{角色1,角色2,角色3} 删:{角色1,角色4,角色5} 改:{角色1,角色2}......
 * String:权限
 * Collection<ConfigAttribute>:角色集合
 */
private static HashMap<String,Collection<ConfigAttribute>> map = null;
/**
 * 初始化 [权限 对应的所属者]
 select t1.name as name,t3.url as url
 from role t1,role_permission t2,permission t3
 where (t1.id=t2.role_id and t2.permission_id=t3.id)
 */
public void loadResourceDefine() {
    map = new HashMap<>(16);
    //初始化 [权限 对应的所有角色] 例如:增:{角色1,角色2,角色3} 删:{角色1,角色4,角色5} 改:{角色1,角色2}......
    List<Permission> rolePermissons = permissionMapper.getRolePermissions();


    //某个资源 可以被哪些角色访问
    for (Permission rolePermisson : rolePermissons) {
        //权限
        String url = rolePermisson.getUrl();
        //角色
        String roleName = rolePermisson.getName();
        //配置属性
        ConfigAttribute role = new SecurityConfig(roleName);


        //例如:增:{角色1,角色2,角色3} 删:{角色1,角色4,角色5} 改:{角色1,角色2}......
        /**
         * 进行校验:原有权限添加新的角色 or 新增了权限添加相应角色
         */
        if(map.containsKey(url)){
            map.get(url).add(role);
        }else{//有新的权限的话 也要把对应所属者的角色 添加到map字典中
            List<ConfigAttribute> list = new ArrayList<>();
            list.add(role);
            map.put(url,list);
        }
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值