Oauth2介绍:Oauth2是为用户资源的授权定义了一个安全、开放及简单的标准,第三方无需知道用户的账号及密码,就可获取到用户的授权信息,并且这是安全的。

简单的来说,当用户登陆网站的时候,需要账号和密码,但是你没有账号和密码,你需要注册网站的账号和密码,可是你不想注册,如果我有(qq,github,微博,facebook)第三方网站的账号,直接登陆当前网站访问网站的资源就好了?有没有这种实现呢?

答案是yes,当然为统一规范,其中就用到 oauh2。

ouah2有4中实现模式(参考阮一峰的 http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html)

(1)授权码模式

用户访问网站登陆的时候,选择qq登陆,facebook登陆,或者微博登陆的时候,网站将你导入qq,facebook,微博的登陆页面(也就是认证服务器)输入账号和密码,当授权成功时,将获取唯一的授权码(Auth code),然后客户端拿到这个Auth code附上早先的重定向url,向认qq,facebook,请求token,向认证服务器(qq,facebook)提交的请求头核对授权码和重定向url,确认无误,返回token和更新令牌

(2)密码模式

用户向客户端提高自己的账号和密码。客户端使用这些信息,向服务器提供商索要授权码,认证服务器认证通过以后,返回令牌,用户通过令牌就可以访问网站的资源

(3)简易模式

不通过第三方应用程序的服务器,直接在浏览器中向认证服务器索要令牌,跳过授权码这个步骤。

(4)客户端模式

指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。

Spring Security介绍:

Spring Security是一个专注于为Java应用程序提供身份验证和授权的框架,使用内部使用Servlet过滤器对url的请求进行过滤,并且在应用程序处理该请求之前进行某些安全处理。 Spring Security提供有若干个过滤器,它们能够拦截Servlet请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性。根据自己的需要,可以使用适当的过滤器来保护自己的应用程序。

Spring Security 与oath2自定义权限控制:

1、创建自己的一个决策器AccessDecisionManager ——AccessDecisionManager在Spring Security Filter(过滤器) 充当权限的判断,即判断某个用户请求某一个url的是否具有权限。实现这个接口,就具有决策管理功能

Spring提供了3个决策管理器,至于这三个管理器是如何工作的请查看SpringSecurity源码

AffirmativeBased 一票通过,只要有一个投票器通过就允许访问

ConsensusBased 有一半以上投票器通过才允许访问资源

UnanimousBased 所有投票器都通过才允许访问

代码如下:

  1. @Component
  2. public class MyAccessDecisionManager implements AccessDecisionManager {
  3.  
  4. protected final Log logger = LogFactory.getLog(getClass());
  5.  
  6. /***
  7. *
  8. *
  9. * @param authentication 登陆系统用户的权限
  10. *
  11. * @param object
  12. * @param configAttributes url的权限
  13. *
  14. * @throws AccessDeniedException
  15. * 配置属性
  16. * @throws InsufficientAuthenticationException
  17. */
  18.  
  19. @Override
  20. public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
  21. throws AccessDeniedException, InsufficientAuthenticationException {
  22. if (null == configAttributes || configAttributes.size() <= 0 || authentication.getAuthorities().size() == 0) {
  23. return;
  24. }
  25. ConfigAttribute c;
  26. String needRole;
  27. for (Iterator<ConfigAttribute> iterable = configAttributes.iterator(); iterable.hasNext();) {
  28. c = iterable.next();
  29. String a = c.getAttribute() == null ? "" : c.getAttribute();
  30. needRole = a.replaceAll("\b", "");
  31. for (GrantedAuthority ga : authentication.getAuthorities()) { // authentication
  32.  
  33. if (needRole.trim().equals(ga.getAuthority()) || ga.getAuthority().equals(AuthoritiesConstants.ADMIN)) { // 循环添加到GrantedAuthority对象中的权限信息集合
  34. return;
  35. }
  36. }
  37. }
  38.  
  39. throw new AccessDeniedException("没有权限访问!");
  40. }
  41.  
  42. @Override
  43. public boolean supports(ConfigAttribute attribute) {
  44. return true;
  45. }
  46.  
  47. @Override
  48. public boolean supports(Class<?> clazz) {
  49. return true;
  50. }
  51.  
  52. }
  1. 2、继承 AbstractSecurityInterceptor 并实现Filter 这里主要是获取用户的权限和获取url路径的权限提供给决策器(AccessDecisionManager)使用
  1. AbstractSecurityInterceptor源码解读:
  1. Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
    .getAttributes(object); //获取url所需要的权限
  1. Authentication authenticated = authenticateIfRequired();//获取用户的权限
  1. this.accessDecisionManager.decide(authenticated, object, attributes); //调用决策器的方法
  1.  
  2. 代码如下:
  1. /**
  2. * <p>
  3. * 访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用
  4. * FilterInvocationSecurityMetadataSource的getAttributes拦截url所需的全部权限
  5. * 在调用授权管理器AccessDecisionManage,如果权限足够,则通过权限认证
  6. * </p>
  7. * Created by develop on 2017/9/1.
  8. */
  9. @Component
  10. public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
  11.  
  12. @Autowired
  13. private MyInvocationSecurityMetadataSourceService securityMetadataSource;
  14.  
  15. protected final Log logger = LogFactory.getLog(getClass());
  16.  
  17. private TokenExtractor tokenExtractor = new BearerTokenExtractor();
  18.  
  19. //注入
  20. @Autowired
  21. public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
  22. super.setAccessDecisionManager(myAccessDecisionManager);
  23. }
  24.  
  25. @Override
  26. public void init(FilterConfig filterConfig) throws ServletException {
  27.  
  28. }
  29.  
  30. @Override
  31. public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  32. FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
  33. logger.debug("用户上下文信息:{}", SecurityContextHolder.getContext().getAuthentication());
  34. logger.debug("url地址:{}",fi.getHttpRequest().getRequestURI());
  35. logger.debug("客户端信息:{}", fi.getHttpRequest());
  36. invoke(fi);
  37. }
  38.  
  39. public void invoke(FilterInvocation fi) throws IOException, ServletException {
  40. InterceptorStatusToken token = null;
  41. if(SecurityContextHolder.getContext().getAuthentication()!=null){ //这里要做非空判断,因为有可能 SecurityContextHolder.getContext().getAuthentication()获取为空,导致后面报错
  42. token=super.beforeInvocation(fi);
  43. }
  44. try {
  45. fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
  46. } finally {
  47. super.afterInvocation(token, null);
  48. }
  49.  
  50. }
  51.  
  52. @Override
  53. public void destroy() {
  54.  
  55. }
  56.  
  57. @Override
  58. public Class<?> getSecureObjectClass() {
  59. return FilterInvocation.class;
  60.  
  61. }
  62.  
  63. @Override
  64. public SecurityMetadataSource obtainSecurityMetadataSource() {
  65. return this.securityMetadataSource;
  66. }
  67. }
  1.  

3、实现FilterInvocationSecurityMetadataSource接口:这个接口提供AbstractSecurityInterceptor需要的url权限 :到这里就可以通过数据库自定义url权限进行拦截了

代码如下:

  1. public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource {
  2.  
  3. private final Logger log = LoggerFactory.getLogger(MyInvocationSecurityMetadataSourceService.class);
  4.  
  5. @Autowired
  6. private SecurityResourcesRepository securityResourcesRepository; //资源对应的路径
  7.  
  8. @Autowired
  9. private SecurityAuthorityRepository securityAuthorityRepository; //资源对应的权限
  10.  
  11. @Autowired
  12. private SecurityAuxiliaryRoleRepository auxiliaryRoleRepository; //主角色对应的副角色
  13.  
  14. private HashMap<String, Collection<ConfigAttribute>> requestMap = null; //存储url的权限
  15.  
  16. private Map<String, List<SecurityResources>> requestHttpMethod = null; //存储url对应的请求
  17.  
  18. //通过@Autowired注入MyInvocationSecurityMetadataSourceService,
  19. //在页面调用loadResourceDefine(),就可以重新刷新权限,因为Spring加载完权限,除非你自己重启服务器,否则这个数据放在内存会一直没有变化,所以得通过这样的方式。
  20. public void loadResourceDefine() {
  21. requestMap = Maps.newHashMap();
  22. requestHttpMethod = Maps.newHashMap();
  23. Collection<ConfigAttribute> array;
  24. ConfigAttribute cfg;
  25. List<SecurityResources> resourcesList = this.securityResourcesRepository.findAll(); //所有的菜单
  26. for (SecurityResources securityResources : resourcesList) { //遍历url对应的资源
  27. List<SecurityAuthority> securityAuthorityList = securityAuthorityRepository.findByResourcesUrl(securityResources.getId().toString());//获取
  28. List<SecurityResources> securityResourcesList = securityResourcesRepository.findByUrl(securityResources.getUrl());
  29. array = Lists.newArrayList();
  30. for (SecurityAuthority securityAuthority : securityAuthorityList) {
  31. cfg = new SecurityConfig(securityAuthority.getAuthorityName()); //获取权限的名字
  32. array.add(cfg); //添加权限的内容
  33. List<SecurityAuxiliaryRole> securityAuxiliaryRoleList =
  34. auxiliaryRoleRepository.findByRoleName(securityAuthority.getAuthorityName());
  35. for (SecurityAuxiliaryRole securityAuxiliaryRole : securityAuxiliaryRoleList) { // 获取主角色对应的副角色
  36. cfg = new SecurityConfig(securityAuxiliaryRole.getAuxiliaryRole());
  37. array.add(cfg);
  38. }
  39. }
  40. //权限的url作为map的key,用ConfigAttribute
  41. requestMap.put(securityResources.getUrl(), array);
  42. requestHttpMethod.put(securityResources.getUrl(), securityResourcesList);
  43. }
  44. }
  45.  
  46. /**
  47. * 此方法是为了判断用户请求的url 是否在权限表中,如果在权限表中,则返回decide方法,用来判断用户是否具有此权限
  48. * 当用户访问url时,通过url获取requestMap的其中的权限。
  49. *
  50. * @param object
  51. * @return
  52. * @throws IllegalArgumentException
  53. */
  54. @Override
  55. public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
  56. if (requestMap == null) loadResourceDefine();
  57. //object 中包含用户请求的request 信息
  58. HttpServletRequest request = ((FilterInvocation) object).getHttpRequest(); //url
  59. AntPathRequestMatcher matcher;
  60. for (Map.Entry<String, Collection<ConfigAttribute>> entry : requestMap
  61. .entrySet()) {
  62. for (int i = 0; i < requestHttpMethod.get(entry.getKey()).size(); i++) { //获取请求
  63. SecurityResources securityResources = requestHttpMethod.get(entry.getKey()).get(i);
  64. if (null != securityResources && null != securityResources.getHttpMethod()) {
  65. matcher = new AntPathRequestMatcher(entry.getKey(), securityResources.getHttpMethod().toUpperCase());
  66. } else {
  67. matcher = new AntPathRequestMatcher(entry.getKey());
  68. }
  69. if (matcher.matches(request) && entry.getValue().size() > 0) {
  70. log.debug("返回url的需要的权限为" + entry.getValue());
  71. return entry.getValue();
  72. }
  73. }
  74. }
  75. return null;
  76. }
  77.  
  78. @Override
  79. public Collection<ConfigAttribute> getAllConfigAttributes() {
  80. return null;
  81. }
  82.  
  83. @Override
  84. public boolean supports(Class<?> clazz) {
  85. return true;
  86. }
  87. }

结束语

这篇文章其实还有很多地方没有写好,比如oauh2的这块还是没有讲清楚,如oauh2的如何配置资源服务器和认证服务器的搭建,权限的配置。Spring Security的拦截器具体实现步骤,用户登陆时权限的拦截实现。

这块我也不是特别熟悉,所以一点带过。不过还是希望这个能帮助大家,解决业务上的一点问题!行千里路,还要读万卷书。只要这样才能诚心诚意做好技术,才是对自己最大的负责。

Spring Security +Oauth2 +Spring boot 动态定义权限的更多相关文章

  1. 微服务下前后端分离的统一认证授权服务,基于Spring Security OAuth2 + Spring Cloud Gateway实现单点登录

    1.  整体架构 在这种结构中,网关就是一个资源服务器,它负责统一授权(鉴权).路由转发.保护下游微服务. 后端微服务应用完全不用考虑权限问题,也不需要引入spring security依赖,就正常的 ...

  2. Spring Security 解析(五) —— Spring Security Oauth2 开发

    Spring Security 解析(五) -- Spring Security Oauth2 开发   在学习Spring Cloud 时,遇到了授权服务oauth 相关内容时,总是一知半解,因此决 ...

  3. Spring Boot 中使用 Spring Security, OAuth2 跨域问题 (自己挖的坑)

    使用 Spring Boot 开发 API 使用 Spring Security + OAuth2 + JWT 鉴权,已经在 Controller 配置允许跨域: @RestController @C ...

  4. 【Spring Cloud & Alibaba 实战 | 总结篇】Spring Cloud Gateway + Spring Security OAuth2 + JWT 实现微服务统一认证授权和鉴权

    一. 前言 hi,大家好~ 好久没更文了,期间主要致力于项目的功能升级和问题修复中,经过一年时间的打磨,[有来]终于迎来v2.0版本,相较于v1.x版本主要完善了OAuth2认证授权.鉴权的逻辑,结合 ...

  5. Spring Security OAuth2 SSO

    通常公司肯定不止一个系统,每个系统都需要进行认证和权限控制,不可能每个每个系统都自己去写,这个时候需要把登录单独提出来 登录和授权是统一的 业务系统该怎么写还怎么写 最近学习了一下Spring Sec ...

  6. springboot+spring security +oauth2.0 demo搭建(password模式)(认证授权端与资源服务端分离的形式)

    项目security_simple(认证授权项目) 1.新建springboot项目 这儿选择springboot版本我选择的是2.0.6 点击finish后完成项目的创建 2.引入maven依赖  ...

  7. Spring Security Oauth2 单点登录案例实现和执行流程剖析

    Spring Security Oauth2 OAuth是一个关于授权的开放网络标准,在全世界得到的广泛的应用,目前是2.0的版本.OAuth2在“客户端”与“服务提供商”之间,设置了一个授权层(au ...

  8. Spring Security OAuth2学习

    什么是 oAuth oAuth 协议为用户资源的授权提供了一个安全的.开放而又简易的标准.与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需 ...

  9. [Spring Cloud实战 | 第六篇:Spring Cloud Gateway+Spring Security OAuth2+JWT实现微服务统一认证授权

    一. 前言 本篇实战案例基于 youlai-mall 项目.项目使用的是当前主流和最新版本的技术和解决方案,自己不会太多华丽的言辞去描述,只希望能勾起大家对编程的一点喜欢.所以有兴趣的朋友可以进入 g ...

随机推荐

  1. Logstash 收集 IIS 日志

    日志样例 查看 IIS 日志配置,选择格式为 W3C(默认字段设置)保存生效. 2016-02-25 01:27:04 112.74.74.124 GET /goods/list/0/1.html - ...

  2. [ActionScript 3.0] 正则表达式

    正则表达式: 正则表达式最早是由数学家Stephen Kleene在对自然语言的递增研究成果的基础上,于1956提出来的.具有完整语法的正则表达式,主要使用在字符串的格式的匹配方面上,后来也逐渐应用到 ...

  3. Postman使用手册1——导入导出和发送请求查看响应

    导读: 现在的web和移动开发,常常会调用服务器提供restful接口进行数据请求,为了调试,一般会先用工具进行测试,通过测试后才开始在开发中使用.这里介绍一下如何在chrome浏览器利用postma ...

  4. P4173 残缺的字符串

    题目链接 题意分析 啥 ? ? ? \(FFT\)做字符串匹配 可是就是这样 我们定义匹配函数 我们定义\(A\)是匹配串 \(B\)是被匹配串 我们当前到达\(B\)串的\(x\)位置 \[P(x) ...

  5. Little Sub and Piggybank (杭师大第十二届校赛G题) DP

    题目传送门 题意:每天能往存钱罐加任意实数的钱,每天不能多于起那一天放的钱数.如果某一天的钱数恰好等于那天的特价商品,则可以买,求最后的最大快乐值. 思路:先来一段来自出题人的题解: 显然的贪心:如果 ...

  6. redis实现 msetex和 getdel命令

    1.redis本身不提供 msetex命令(批量增加key并设置过期时间) class RedisExtend { private static final Logger logger = Logge ...

  7. python学习 条件控制

    if - else  if 条件a : 内容a else : 内容 非a if - elif - else  if 条件a : 内容a elif 条件b: 内容b-a else : 内容 非(a∪b) ...

  8. java NIO学前准备

    之前一直对NIO感兴趣,无奈对IO的很多概念很模糊,所以对于NIO的学习也是一直半解,最近在网上查阅了很多资料,发现有很多概念是需要反复理解的,有的时候甚至当时理解了,但一段时间后又忘记了,所以决定自 ...

  9. KOA 与 CO 实现浅析

    KOA 与 CO 的实现都非常的短小精悍,只需要花费很短的时间就可以将源代码通读一遍.以下是一些浅要的分析. 如何用 node 实现一个 web 服务器 既然 KOA 实现了 web 服务器,那我们就 ...

  10. orcale 之 pl/sql

    基本结构 不多说直接来看下它的结构: DECLARE -- 此处声明一些变量.常量.或者用户自定的数据类型 -- 这一部分是可选的,如果不需要可以不写 BEGIN -- 程序的主体,这里可以写一些合法 ...