Spring Security Oauth2 添加自定义过滤器和oauth2认证后API权限控制

Spring Security Oauth2 添加自定义过滤器和oauth2认证后API权限控制

在搭建完 spring-security-oauth2 整个微服务框架后,来了一个需求:

每个微服务都需要对访问进行鉴权,每个微服务应用都需要明确当前访问用户和他的权限。

auth 系统的主要功能是授权认证和鉴权。

授权认证已经完成,那么如何对用户的访问进行鉴权呢?

首先需要明确什么时候发生鉴权?

鉴权发生在用户已经认证后携带了 access_token 信息但还没用访问到目标资源的时候。

知道了鉴权发生的时间,需要明白怎么鉴权?

我的想法是添加一个用于鉴权的过滤器,Spring Security 默认的过滤器链(官网):

别名类名称Namespace Element or Attribute
CHANNEL_FILTERChannelProcessingFilterhttp/intercept-url@requires-channel
SECURITY_CONTEXT_FILTERSecurityContextPersistenceFilterhttp
CONCURRENT_SESSION_FILTERConcurrentSessionFiltersession-management/concurrency-control
HEADERS_FILTERHeaderWriterFilterhttp/headers
CSRF_FILTERCsrfFilterhttp/csrf
LOGOUT_FILTERLogoutFilterhttp/logout
X509_FILTERX509AuthenticationFilterhttp/x509
PRE_AUTH_FILTERAbstractPreAuthenticatedProcessingFilter( Subclasses)N/A
CAS_FILTERCasAuthenticationFilter N/A
FORM_LOGIN_FILTERUsernamePasswordAuthenticationFilterhttp/form-login
BASIC_AUTH_FILTERBasicAuthenticationFilterhttp/http-basic
SERVLET_API_SUPPORT_FILTERSecurityContextHolderAwareRequestFilterhttp/@servlet-api-provision
JAAS_API_SUPPORT_FILTERJaasApiIntegrationFilterhttp/@jaas-api-provision
REMEMBER_ME_FILTERRememberMeAuthenticationFilterhttp/remember-me
ANONYMOUS_FILTERAnonymousAuthenticationFilterhttp/anonymous
SESSION_MANAGEMENT_FILTERSessionManagementFiltersession-management
EXCEPTION_TRANSLATION_FILTERExceptionTranslationFilterhttp
FILTER_SECURITY_INTERCEPTORFilterSecurityInterceptorhttp
SWITCH_USER_FILTERSwitchUserFilterN/A

过滤器顺序从上到下

FilterSecurityInterceptor 是 filterchain 中比较复杂,也是比较核心的过滤器,主要负责web应用安全授权的工作。

我想添加的过滤器是添加在 FilterSecurityInterceptor 之后。

Oauth2FilterSecurityInterceptor 是模仿 FilterSecurityInterceptor 实现,继承 AbstractSecurityInterceptor 和实现 Filter 接口。

整个过程需要依赖 AuthenticationManager、AccessDecisionManager 和 FilterInvocationSecurityMetadataSource。

  • AuthenticationManager是认证管理器,实现用户认证的入口;
  • AccessDecisionManager是访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源;
  • FilterInvocationSecurityMetadataSource是资源源数据定义,即定义某一资源可以被哪些角色访问。

自定义鉴权过滤器 Oauth2FilterSecurityInterceptor 的实现

package com.fengxuechao.examples.auth.authorization;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.web.FilterInvocation;

import javax.servlet.*;
import java.io.IOException;

/**
 * 比较核心的过滤器: 主要负责web应用鉴权的工作。
 * 需要依赖:
 * - AuthenticationManager:认证管理器,实现用户认证的入口;
 * - AccessDecisionManager:访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源;
 * - FilterInvocationSecurityMetadataSource:资源源数据定义,即定义某一资源可以被哪些角色访问.
 *
 * @author fengxuechao
 * @version 0.1
 * @date 2019/6/17
 */
@Slf4j
public class Oauth2FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {

    private Oauth2FilterInvocationSecurityMetadataSource securityMetadataSource;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        if (log.isInfoEnabled()) {
            log.info("Oauth2FilterSecurityInterceptor init");
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (log.isInfoEnabled()) {
            log.info("Oauth2FilterSecurityInterceptor doFilter");
        }
        FilterInvocation filterInvocation = new FilterInvocation(request, response, chain);
        invoke(filterInvocation);
    }

    public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
        // filterInvocation里面有一个被拦截的url
        // 里面调用 Oauth2AccessDecisionManager 的 getAttributes(Object object) 这个方法获取 filterInvocation 对应的所有权限
        // 再调用 Oauth2AccessDecisionManager 的 decide方法来校验用户的权限是否足够
        InterceptorStatusToken interceptorStatusToken = super.beforeInvocation(filterInvocation);
        try {
            // 执行下一个拦截器
            filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
        } finally {
            super.afterInvocation(interceptorStatusToken, null);
        }
    }

    @Override
    public void destroy() {

    }

    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    /**
     * 资源源数据定义,设置为自定义的 SecureResourceFilterInvocationDefinitionSource
     *
     * @return
     */
    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return securityMetadataSource;
    }

    public void setOauth2AccessDecisionManager(Oauth2AccessDecisionManager accessDecisionManager) {
        super.setAccessDecisionManager(accessDecisionManager);
    }

    @Override
    public void setAuthenticationManager(AuthenticationManager authenticationManager) {
        super.setAuthenticationManager(authenticationManager);
    }

    public void setSecurityMetadataSource(Oauth2FilterInvocationSecurityMetadataSource securityMetadataSource) {
        this.securityMetadataSource = securityMetadataSource;
    }
}

看下父类的 beforeInvocation 方法,其中省略了一些不重要的代码片段:

public abstract class AbstractSecurityInterceptor implements InitializingBean, ApplicationEventPublisherAware, MessageSourceAware {
   protected InterceptorStatusToken beforeInvocation(Object object) {
		// 代码省略
        
        // 根据 SecurityMetadataSource 获取配置的权限属性
		Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
				.getAttributes(object);

		// 代码省略

        // 判断是否需要对认证实体重新认证,默认为否
		Authentication authenticated = authenticateIfRequired();

		// Attempt authorization
		try {
			// 决策管理器开始决定是否授权,如果授权失败,直接抛出 AccessDeniedException
	        this.accessDecisionManager.decide(authenticated, object, attributes);
		}
		catch (AccessDeniedException accessDeniedException) {
			publishEvent(new AuthorizationFailureEvent(object, attributes, authenticated,
					accessDeniedException));

			throw accessDeniedException;
		}

		// 代码省略
   }
}

自定义资源源数据定义 Oauth2FilterInvocationSecurityMetadataSource

package com.fengxuechao.examples.auth.authorization;

import com.fengxuechao.examples.auth.service.UserRolePermissionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * 资源源数据定义,即定义某一资源可以被哪些角色访问
 *
 * @author fengxuechao
 * @version 0.1
 * @date 2019/6/14
 */
@Slf4j
@Component
public class Oauth2FilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource, InitializingBean {

    private UserRolePermissionService service;

    public Oauth2FilterInvocationSecurityMetadataSource(UserRolePermissionService service) {
        this.service = service;
    }

    
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        if ("/user/profile".equals(((FilterInvocation) object).getRequestUrl())) {
            // [/user/profile] 不需要鉴权
            return null;
        }
        /*if (object instanceof FilterInvocation) {
            FilterInvocation fi = (FilterInvocation) object;
            String requestUrl = fi.getRequestUrl();
            // 返回请求所需的权限
            List<Role> roleList = service.findRoleListByPermissionUrl(requestUrl);
            String[] roleArray = new String[roleList.size()];
            roleArray = roleList.toArray(roleArray);
            return SecurityConfig.createList(roleArray);
        }
        return Collections.EMPTY_LIST;*/
        return SecurityConfig.createList("ROLE_ADMIN");
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }
}

为了调试的方便,直接定死任何访问请求都需要管理员权限(/user/profile 除外),调试通过后,再往里面添加业务逻辑代码。

自定义决策管理器 Oauth2AccessDecisionManager

package com.fengxuechao.examples.auth.authorization;

import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Iterator;

/**
 * 访问决策器,决定某个用户具有的角色,是否有足够的权限去访问某个资源
 *
 * @author fengxuechao
 * @version 0.1
 * @date 2019/6/14
 */
@Slf4j
@Component
public class Oauth2AccessDecisionManager implements AccessDecisionManager {

    /**
     * @param authentication   用户凭证
     * @param resource         资源 URL
     * @param configAttributes 资源 URL 所需要的权限
     * @throws AccessDeniedException               资源拒绝访问
     * @throws InsufficientAuthenticationException 用户凭证不符
     */
    @Override
    public void decide(Authentication authentication, Object resource, Collection<ConfigAttribute> configAttributes)
            throws AccessDeniedException, InsufficientAuthenticationException {
        log.info("[决策管理器]:开始判断请求 {} 需要的权限", ((FilterInvocation) resource).getRequestUrl());
        if (configAttributes == null || configAttributes.isEmpty()) {
            log.info("[决策管理器]:请求 {} 无需权限", ((FilterInvocation) resource).getRequestUrl());
            return;
        }
        log.info("[决策管理器]:请求 {} 需要的权限 - {}", ((FilterInvocation) resource).getRequestUrl(), configAttributes);
        // 判断用户所拥有的权限,是否符合对应的Url权限,用户权限是实现 UserDetailsService#loadUserByUsername 返回用户所对应的权限
        Iterator<ConfigAttribute> ite = configAttributes.iterator();
        log.info("[决策管理器]:用户 {} 拥有的权限 - {}", authentication.getName(), authentication.getAuthorities());
        while (ite.hasNext()) {
            ConfigAttribute neededAuthority = ite.next();
            String neededAuthorityStr = neededAuthority.getAttribute();
            for (GrantedAuthority existingAuthority : authentication.getAuthorities()) {
                if (neededAuthorityStr.equals(existingAuthority.getAuthority())) {
                    return;
                }
            }
        }
        log.info("[决策管理器]:用户 {} 没有访问资源 {} 的权限!", authentication.getName(), ((FilterInvocation) resource).getRequestUrl());
        throw new AccessDeniedException("权限不足!");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    /**
     * 是否支持 FilterInvocationSecurityMetadataSource 需要将这里的false改为true
     *
     * @param clazz
     * @return
     */
    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}

配置自定义鉴权过滤器 Oauth2FilterSecurityInterceptor 在 Spring Security 过滤器链中的位置

package com.fengxuechao.examples.auth.config;

import com.fengxuechao.examples.auth.authorization.Oauth2AccessDecisionManager;
import com.fengxuechao.examples.auth.authorization.Oauth2FilterInvocationSecurityMetadataSource;
import com.fengxuechao.examples.auth.authorization.Oauth2FilterSecurityInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

/**
 * @author fengxuechao
 * @version 0.1
 * @date 2019/5/8
 */
@Slf4j
@EnableResourceServer
@Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    AuthenticationManager manager;

    @Autowired
    Oauth2AccessDecisionManager accessDecisionManager;

    @Autowired
    Oauth2FilterInvocationSecurityMetadataSource securityMetadataSource;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated();
        http.addFilterAfter(createApiAuthenticationFilter(), FilterSecurityInterceptor.class);
    }

    /**
     * API权限控制
     * 过滤器优先度在 FilterSecurityInterceptor 之后
     * spring-security 的默认过滤器列表见 https://docs.spring.io/spring-security/site/docs/5.0.0.M1/reference/htmlsingle/#ns-custom-filters
     *
     * @return
     */
    private Oauth2FilterSecurityInterceptor createApiAuthenticationFilter() {
        Oauth2FilterSecurityInterceptor interceptor = new Oauth2FilterSecurityInterceptor();
        interceptor.setAuthenticationManager(manager);
        interceptor.setAccessDecisionManager(accessDecisionManager);
        interceptor.setSecurityMetadataSource(securityMetadataSource);
        return interceptor;
    }
}

配置用户权限

package com.fengxuechao.examples.auth.userdetails;

import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

/**
 * @author fengxuechao
 * @version 0.1
 * @date 2019/5/15
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new User(username, "123456", AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
    }
}

演示结果

用户拥有资源所需权限

请求:

GET http://localhost:8080/order/1

HTTP/1.1 200 
X-Application-Context: application:inMemory
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Frame-Options: DENY
Content-Type: text/plain;charset=UTF-8
Content-Length: 12
Date: Tue, 18 Jun 2019 01:50:48 GMT

order id : 1

Response code: 200; Time: 57ms; Content length: 12 bytes

日志:

2019-06-18 09:50:48.955  INFO 5288 --- [nio-8080-exec-3] .f.e.a.a.Oauth2FilterSecurityInterceptor : Oauth2FilterSecurityInterceptor doFilter
2019-06-18 09:50:48.955 DEBUG 5288 --- [nio-8080-exec-3] .f.e.a.a.Oauth2FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /order/1; Attributes: [ROLE_USER]
2019-06-18 09:50:48.956 DEBUG 5288 --- [nio-8080-exec-3] .f.e.a.a.Oauth2FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.oauth2.provider.OAuth2Authentication@f5aeefea: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=127.0.0.1, tokenType=bearertokenValue=<TOKEN>; Granted Authorities: ROLE_USER
2019-06-18 09:50:48.956  INFO 5288 --- [nio-8080-exec-3] c.f.e.a.a.Oauth2AccessDecisionManager    : [决策管理器]:开始判断请求 /order/1 需要的权限
2019-06-18 09:50:48.956  INFO 5288 --- [nio-8080-exec-3] c.f.e.a.a.Oauth2AccessDecisionManager    : [决策管理器]:请求 /order/1 需要的权限 - [ROLE_USER]
2019-06-18 09:50:48.956  INFO 5288 --- [nio-8080-exec-3] c.f.e.a.a.Oauth2AccessDecisionManager    : [决策管理器]:用户 user 拥有的权限 - [ROLE_USER]
2019-06-18 09:50:48.956 DEBUG 5288 --- [nio-8080-exec-3] .f.e.a.a.Oauth2FilterSecurityInterceptor : Authorization successful
2019-06-18 09:50:48.957 DEBUG 5288 --- [nio-8080-exec-3] .f.e.a.a.Oauth2FilterSecurityInterceptor : RunAsManager did not change Authentication object

用户没有资源所需权限

请求:

GET http://localhost:8080/order/1

HTTP/1.1 403 
Cache-Control: no-store
Pragma: no-cache
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: DENY
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 18 Jun 2019 01:44:49 GMT

{
  "error": "access_denied",
  "error_description": "权限不足!"
}

Response code: 403; Time: 35ms; Content length: 53 bytes

日志:

2019-06-18 09:44:44.684  INFO 10624 --- [nio-8080-exec-2] .f.e.a.a.Oauth2FilterSecurityInterceptor : Oauth2FilterSecurityInterceptor doFilter
2019-06-18 09:44:44.685 DEBUG 10624 --- [nio-8080-exec-2] .f.e.a.a.Oauth2FilterSecurityInterceptor : Public object - authentication not attempted
2019-06-18 09:44:49.448  INFO 10624 --- [nio-8080-exec-6] .f.e.a.a.Oauth2FilterSecurityInterceptor : Oauth2FilterSecurityInterceptor doFilter
2019-06-18 09:44:49.449 DEBUG 10624 --- [nio-8080-exec-6] .f.e.a.a.Oauth2FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /order/1; Attributes: [ROLE_ADMIN]
2019-06-18 09:44:49.449 DEBUG 10624 --- [nio-8080-exec-6] .f.e.a.a.Oauth2FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.oauth2.provider.OAuth2Authentication@22d262ad: Principal: org.springframework.security.core.userdetails.User@36ebcb: Username: user; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: remoteAddress=127.0.0.1, tokenType=bearertokenValue=<TOKEN>; Granted Authorities: ROLE_USER
2019-06-18 09:44:49.450  INFO 10624 --- [nio-8080-exec-6] c.f.e.a.a.Oauth2AccessDecisionManager    : [决策管理器]:开始判断请求 /order/1 需要的权限
2019-06-18 09:44:49.450  INFO 10624 --- [nio-8080-exec-6] c.f.e.a.a.Oauth2AccessDecisionManager    : [决策管理器]:请求 /order/1 需要的权限 - [ROLE_ADMIN]
2019-06-18 09:44:49.450  INFO 10624 --- [nio-8080-exec-6] c.f.e.a.a.Oauth2AccessDecisionManager    : [决策管理器]:用户 user 拥有的权限 - [ROLE_USER]
2019-06-18 09:44:49.451  INFO 10624 --- [nio-8080-exec-6] c.f.e.a.a.Oauth2AccessDecisionManager    : [决策管理器]:用户 user 没有访问资源 /order/1 的权限!

返回结果和日志符合期望结果

参考资源

http://www.spring4all.com/article/422

  • 9
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
本项目示例基于spring boot 最新版本(2.1.9)实现,Spring Boot、Spring Cloud 学习示例,将持续更新…… 在基于Spring Boot、Spring Cloud 分布微服务开发过程中,根据实际项目环境,需要选择、集成符合项目需求的各种组件和积累各种解决方案。基于这样的背景下,我开源了本示例项目,方便大家快速上手Spring Boot、Spring Cloud 。 每个示例都带有详细的介绍文档、作者在使用过程中踩过的坑、解决方案及参考资料,方便快速上手为你提供学习捷径,少绕弯路,提高开发效率。 有需要写关于spring boot、spring cloud示例,可以给我提issue哦 ## 项目介绍 spring boot demo 是一个Spring Boot、Spring Cloud的项目示例,根据市场主流的后端技术,共集成了30+个demo,未来将持续更新。该项目包含helloworld(快速入门)、web(ssh项目快速搭建)、aop(切面编程)、data-redis(redis缓存)、quartz(集群任务实现)、shiro(权限管理)、oauth2(四种认证模式)、shign(接口参数防篡改重放)、encoder(用户密码设计)、actuator(服务监控)、cloud-config(配置中心)、cloud-gateway(服务网关)、email(邮件发送)、cloud-alibaba(微服务全家桶)等模块 ### 开发环境 - JDK1.8 + - Maven 3.5 + - IntelliJ IDEA ULTIMATE 2019.1 - MySql 5.7 + ### Spring Boot 模块 模块名称|主要内容 ---|--- helloworld|[spring mvc,Spring Boot项目创建,单元测试](https://github.com/smltq/spring-boot-demo/blob/master/helloworld/HELP.md) web|[ssh项目,spring mvc,过滤器,拦截器,监视器,thymeleaf,lombok,jquery,bootstrap,mysql](https://github.com/smltq/spring-boot-demo/blob/master/web/HELP.md) aop|[aop,正则,前置通知,后置通知,环绕通知](https://github.com/smltq/spring-boot-demo/blob/master/aop/HELP.md) data-redis|[lettuce,redis,session redis,YAML配置,连接池,对象存储](https://github.com/smltq/spring-boot-demo/blob/master/data-redis/HELP.md) quartz|[Spring Scheduler,Quartz,分布式调度,集群,mysql持久化等](https://github.com/smltq/spring-boot-demo/blob/master/quartz/HELP.md) shiro|[授权、认证、加解密、统一异常处理](https://github.com/smltq/spring-boot-demo/blob/master/shiro/HELP.md) sign|[防篡改、防重放、文档自动生成](https://github.com/smltq/spring-boot-demo/blob/master/sign/HELP.md) security|[授权、认证、加解密、mybatis plus使用](https://github.com/smltq/spring-boot-demo/blob/master/security/HELP.md) mybatis-plus-generator|[基于mybatisplus代码自动生成](https://github.com/smltq/spring-boot-demo/blob/master/mybatis-plus-generator) mybatis-plus-crud|[基于mybatisplus实现数据库增、册、改、查](https://github.com/smltq/spring-boot-demo/blob/master/mybatis-plus-crud) encoder|[主流加密算法介绍、用户加密算法推荐](https://github.com/smltq/spring-boot-demo/blob/master/encoder/HELP.md) actuator|[autuator介绍](https://github.com/smltq/spring-boot-demo/blob/master/actuator/README.md) admin|[可视化服务监控、使用](https://github.com/smltq/spring-boot-demo/blob/master/admin/README.md) security-oauth2-credentials|[oauth2实现密码模式、客户端模式](https://github.com/smltq/spring-boot-demo/blob/master/security-oauth2-credentials/README.md) security-oauth2-auth-code|[基于spring boot实现oauth2授权模式](https://github.com/smltq/spring-boot-demo/blob/master/security-oauth2-auth-code/README.md) mybatis-multi-datasource|[mybatis、数据库集群、读写分离、读库负载均衡](https://github.com/smltq/spring-boot-demo/blob/master/mybatis-multi-datasource) template-thymeleaf|[thymeleaf实现应用国际化示例](https://github.com/smltq/spring-boot-demo/blob/master/template-thymeleaf) mq-redis|[redis之mq实现,发布订阅模式](https://github.com/smltq/spring-boot-demo/blob/master/mq-redis) email|[email实现邮件发送](https://github.com/smltq/spring-boot-demo/blob/master/email) jGit|[java调用git命令、jgit使用等](https://github.com/smltq/spring-boot-demo/blob/master/jGit) webmagic|[webmagic实现某电影网站爬虫示例](https://github.com/smltq/spring-boot-demo/blob/master/webmagic) netty|[基于BIO、NIO等tcp服务器搭建介绍](https://github.com/smltq/spring-boot-demo/blob/master/netty) ### Spring Cloud 模块 模块名称|主要内容 ---|--- cloud-oauth2-auth-code|[基于spring cloud实现oath2授权模式](https://github.com/smltq/spring-boot-demo/blob/master/cloud-oauth2-auth-code) cloud-gateway|[API主流网关、gateway快速上手](https://github.com/smltq/spring-boot-demo/blob/master/cloud-gateway) cloud-config|[配置中心(服务端、客户端)示例](https://github.com/smltq/spring-boot-demo/blob/master/cloud-config) cloud-feign|[Eureka服务注册中心、负载均衡、声明式服务调用](https://github.com/smltq/spring-boot-demo/blob/master/cloud-feign) cloud-hystrix|[Hystrix服务容错、异常处理、注册中心示例](https://github.com/smltq/spring-boot-demo/blob/master/cloud-hystrix) cloud-zuul|[zuul服务网关、过滤器、路由转发、服务降级、负载均衡](https://github.com/smltq/spring-boot-demo/blob/master/cloud-zuul) cloud-alibaba|[nacos服务中心、配置中心、限流等使用(系列示例整理中...)](https://github.com/smltq/spring-boot-demo/blob/master/cloud-alibaba) #### Spring Cloud Alibaba 模块 模块名称|主要内容 ---|--- nacos|[Spring Cloud Alibaba(一)如何使用nacos服务注册和发现](https://github.com/smltq/spring-boot-demo/blob/master/cloud-alibaba/README1.md) config|[Spring Cloud Alibaba(二)配置中心多项目、多配置文件、分目录实现](https://github.com/smltq/spring-boot-demo/blob/master/cloud-alibaba/README2.md) Sentinel|[Spring Cloud Alibaba(三)Sentinel之熔断降级](https://github.com/smltq/spring-boot-demo/blob/master/cloud-alibaba/README3.md) Dubbo|[Spring Cloud Alibaba(四)Spring Cloud与Dubbo的融合](https://github.com/smltq/spring-boot-demo/blob/master/cloud-alibaba/README4.md) RocketMQ|[Spring Cloud Alibaba(五)RocketMQ 异步通信实现](https://github.com/smltq/spring-boot-demo/blob/master/cloud-alibaba/README5.md) ### 其它 模块名称|主要内容 ---|--- leetcode|[力扣题解目录](https://github.com/smltq/spring-boot-demo/blob/master/leetcode) ## Spring Boot 概述 Spring Boot简化了基于Spring的应用开发,通过少量的代码就能创建一个独立的、产品级别的Spring应用。 Spring Boot为Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数Spring Boot应用只需要很少的Spring配置。 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Sprin
循序渐进,学习Spring Boot、Spring Boot & Shiro、Spring Cloud、Spring Security & Spring Security OAuth2,博客Spring系列源码 一、Spring Boot教程 开启Spring Boot Spring Boot基础配置 Spring Boot中使用MyBatis Spring Boot中使用JdbcTemplate Spring Boot MyBatis配置Druid多数据源 Spring Boot JdbcTemplate配置Druid多数据源 Spring Boot AOP记录用户操作日志 Spring Boot中使用thymeleaf Spring Boot中使用Redis缓存数据 Spring Boot中使用Ehcache缓存数据 Spring Boot中的JSON技术 Spring Boot中编写单元测试 Spring Boot整合Swagger2构建RESTful API 使用Actuator监控Spring Boot应用 使用Spring Boot发送邮件 使用Spring Boot Admin监控服务 Spring Boot Devtools热部署 Spring Boot logback日志配置 Spring Boot项目打包成war包 Linux下部署Spring Boot jar Spring Boot中使用Jsoup防御XSS攻击 Spring Boot异常处理 Spring Boot中使用过滤器和拦截器 Spring Boot整合MyBatis通用Mapper和PageHelper 深入学习Spring Boot自动装配 深入学习Spring Boot中的SpringApplication Spring Boot配合Hibernate Validator参数校验 自定义Spring Boot 内容协商 Spring Boot 中处理跨域 Spring Boot 中的异步调用 Spring Boot 整合Kafka Spring Boot整合Mongo DB Spring Boot 2.0 WebFlux编程 Spring Boot WebFlux增删改查样例 二、Spring Boot & Shiro教程 Spring Boot Shiro用户认证 Spring Boot Shiro Remember Me Spring Boot Shiro权限控制 Spring Boot Shiro Redis Spring Boot Shiro Ehcache Spring Boot Thymeleaf中使用Shiro标签 Spring Boot Shiro在线会话管理 Spring Boot Shiro整合JWT 三、Spring Boot & Security教程 Spring Boot中开启Spring Security Spring Security自定义用户认证 Spring Security添加图形验证码 Spring Security添加记住我功能 Spring Security短信验证码登录 Spring Security Session管理 Spring Security退出登录 Spring Security权限控制 Spring Security OAuth2入门 Spring Security OAuth2自定义Token获取方式 Spring Security OAuth2自定义令牌配置 Spring Security OAuth2单点登录 四、Spring Cloud教程 初识Spring Cloud与微服务 Spring Cloud Eureka服务治理 Spring Cloud Ribbon客户端负载均衡 Spring Cloud Hystrix服务容错 Spring Cloud Hystrix Dashboard仪表盘 Spring Cloud Hystrix Dashboard仪表盘 & RabbitMQ Spring Cloud Feign 声明式服务调用 Spring Cloud Zuul服务网关 Spring Cloud Config统一配置管理 使用Spring Cloud Bus刷新配置 使用Spring Cloud Sleuth跟踪微服务 Spring Cloud Consul服务治理 五、Spring Boot && Dubbo教程 Spring Boot整合Dubbo&Zookeeper; 监控Dubbo服务 Dubbo的高可用

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值