基于Spring Security的Oauth2授权实现方法

作者:z57354658 时间:2022-12-19 16:52:18 

前言

经过一段时间的学习Oauth2,在网上也借鉴学习了一些大牛的经验,推荐在学习的过程中多看几遍阮一峰的《理解OAuth 2.0》,经过对Oauth2的多种方式的实现,个人推荐Spring Security和Oauth2的实现是相对优雅的,理由如下:

1、相对于直接实现Oauth2,减少了很多代码量,也就减少的查找问题的成本。

2、通过调整配置文件,灵活配置Oauth相关配置。

3、通过结合路由组件(如zuul),更好的实现微服务权限控制扩展。

Oauth2概述

oauth2根据使用场景不同,分成了4种模式

  • 授权码模式(authorization code)

  • 简化模式(implicit)

  • 密码模式(resource owner password credentials)

  • 客户端模式(client credentials)

在项目中我们通常使用授权码模式,也是四种模式中最复杂的,通常网站中经常出现的微博,qq第三方登录,都会采用这个形式。

Oauth2授权主要由两部分组成:

  • Authorization server:认证服务

  • Resource server:资源服务

在实际项目中以上两个服务可以在一个服务器上,也可以分开部署。

准备阶段

核心maven依赖如下


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

<dependency>
 <groupId>com.fasterxml.jackson.datatype</groupId>
 <artifactId>jackson-datatype-joda</artifactId>
</dependency>
<dependency>
 <groupId>org.thymeleaf.extras</groupId>
 <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.security.oauth</groupId>
 <artifactId>spring-security-oauth2</artifactId>
</dependency>

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

token的存储主流有三种方式,分别为内存、redis和数据库,在实际项目中通常使用redis和数据库存储。个人推荐使用mysql数据库存储。

初始化数据结构、索引和数据SQL语句如下:


--
-- Oauth sql -- MYSQL
--

Drop table if exists oauth_client_details;
create table oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
resource_ids VARCHAR(255),
client_secret VARCHAR(255),
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information TEXT,
autoapprove VARCHAR (255) default 'false'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Drop table if exists oauth_access_token;
create table oauth_access_token (
token_id VARCHAR(255),
token BLOB,
authentication_id VARCHAR(255),
user_name VARCHAR(255),
client_id VARCHAR(255),
authentication BLOB,
refresh_token VARCHAR(255)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Drop table if exists oauth_refresh_token;
create table oauth_refresh_token (
token_id VARCHAR(255),
token BLOB,
authentication BLOB
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Drop table if exists oauth_code;
create table oauth_code (
code VARCHAR(255),
authentication BLOB
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- Add indexes
create index token_id_index on oauth_access_token (token_id);
create index authentication_id_index on oauth_access_token (authentication_id);
create index user_name_index on oauth_access_token (user_name);
create index client_id_index on oauth_access_token (client_id);
create index refresh_token_index on oauth_access_token (refresh_token);
create index token_id_index on oauth_refresh_token (token_id);
create index code_index on oauth_code (code);

-- INSERT DEFAULT DATA
INSERT INTO `oauth_client_details` VALUES ('dev', '', 'dev', 'app', 'authorization_code', 'http://localhost:7777/', '', '3600', '3600', '{"country":"CN","country_code":"086"}', 'TAIJI');

核心配置

核心配置主要分为授权应用和客户端应用两部分,如下:

  • 授权应用:即Oauth2授权服务,主要包括Spring Security、认证服务和资源服务两部分配置

  • 客户端应用:即通过授权应用进行认证的应用,多个客户端应用间支持单点登录

授权应用主要配置如下:

application.properties链接已初始化Oauth2的数据库即可

Application启动类,授权服务开启配置和Spring Security配置,如下:


@SpringBootApplication
@AutoConfigureAfter(JacksonAutoConfiguration.class)
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
@EnableAuthorizationServer
public class Application extends WebSecurityConfigurerAdapter {
 public static void main(String[] args) {
   SpringApplication.run(Application.class, args);
 }

// 启动的时候要注意,由于我们在controller中注入了RestTemplate,所以启动的时候需要实例化该类的一个实例
 @Autowired
 private RestTemplateBuilder builder;

// 使用RestTemplateBuilder来实例化RestTemplate对象,spring默认已经注入了RestTemplateBuilder实例
 @Bean
 public RestTemplate restTemplate() {
   return builder.build();
 }

@Configuration
 public class WebMvcConfig extends WebMvcConfigurerAdapter {
   @Override
   public void addViewControllers(ViewControllerRegistry registry) {
     registry.addViewController("/login").setViewName("login");
   }
 }

@Override
 protected void configure(HttpSecurity http) throws Exception {

http.headers().frameOptions().disable();
   http.authorizeRequests()
       .antMatchers("/403").permitAll() // for test
       .antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access", "/appManager").permitAll() // for login
       .antMatchers("/image", "/js/**", "/fonts/**").permitAll() // for login
       .antMatchers("/j_spring_security_check").permitAll()
       .antMatchers("/oauth/authorize").authenticated();
   /*.anyRequest().fullyAuthenticated();*/
   http.formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
       .and()
       .authorizeRequests().anyRequest().authenticated()
       .and().logout().invalidateHttpSession(true)
       .and().sessionManagement().maximumSessions(1).expiredUrl("/login?expired").sessionRegistry(sessionRegistry());
   http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
   http.rememberMe().disable();
   http.httpBasic();

}

}

资源服务开启,如下:


@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
   @Override
   public void configure(HttpSecurity http) throws Exception {
     http.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
   }
 }

OAuth2认证授权服务配置,如下:


@Configuration
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
public static final Logger logger = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);

@Autowired
 private AuthenticationManager authenticationManager;

@Autowired
 private DataSource dataSource;
 @Bean
 public TokenStore tokenStore() {
   return new JdbcTokenStore(dataSource);
 }

@Override
 public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

endpoints.authenticationManager(authenticationManager);
   endpoints.tokenStore(tokenStore());
   // 配置TokenServices参数
   DefaultTokenServices tokenServices = new DefaultTokenServices();
   tokenServices.setTokenStore(endpoints.getTokenStore());
   tokenServices.setSupportRefreshToken(false);
   tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
   tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
   tokenServices.setAccessTokenValiditySeconds( (int) TimeUnit.MINUTES.toSeconds(10)); //分钟
   endpoints.tokenServices(tokenServices);
 }

@Override
 public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
   oauthServer.checkTokenAccess("isAuthenticated()");
   oauthServer.allowFormAuthenticationForClients();
 }

@Bean
 public ClientDetailsService clientDetails() {
   return new JdbcClientDetailsService(dataSource);
 }

@Override
 public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
     clients.withClientDetails(clientDetails());
   /*    
   *    基于内存配置项
   *    clients.inMemory()
       .withClient("community")
       .secret("community")
       .authorizedGrantTypes("authorization_code").redirectUris("http://tech.taiji.com.cn/")
       .scopes("app").and() .withClient("dev")
       .secret("dev")
       .authorizedGrantTypes("authorization_code").redirectUris("http://localhost:7777/")
       .scopes("app");*/
 }
}

客户端应用主要配置如下:

application.properties中Oauth2配置,如下


security.oauth2.client.clientId=dev
security.oauth2.client.clientSecret=dev
security.oauth2.client.accessTokenUri=http://localhost:9999/oauth/token
security.oauth2.client.userAuthorizationUri=http://localhost:9999/oauth/authorize
security.oauth2.resource.loadBalanced=true
security.oauth2.resource.userInfoUri=http://localhost:9999/me
security.oauth2.resource.logout.url=http://localhost:9999/revoke-token
security.oauth2.default.roleName=ROLE_USER

Oauth2Config配置,授权Oauth2Sso配置和Spring Security配置,如下:


@Configuration
@EnableOAuth2Sso
public class Oauth2Config extends WebSecurityConfigurerAdapter{
@Autowired
CustomSsoLogoutHandler customSsoLogoutHandler;

@Autowired
OAuth2ClientContext oauth2ClientContext;

@Bean
public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
  StrictHttpFirewall firewall = new StrictHttpFirewall();
  firewall.setAllowUrlEncodedSlash(true);
  firewall.setAllowSemicolon(true);
  return firewall;
}

@Bean
@ConfigurationProperties("security.oauth2.client")
public AuthorizationCodeResourceDetails taiji() {
return new AuthorizationCodeResourceDetails();
}

@Bean
public CommunitySuccessHandler customSuccessHandler() {
CommunitySuccessHandler customSuccessHandler = new CommunitySuccessHandler();
customSuccessHandler.setDefaultTargetUrl("/");
return customSuccessHandler;
}

@Bean
public CustomFailureHandler customFailureHandler() {
CustomFailureHandler customFailureHandler = new CustomFailureHandler();
customFailureHandler.setDefaultFailureUrl("/index");
return customFailureHandler;
}

@Bean
@Primary
@ConfigurationProperties("security.oauth2.resource")
public ResourceServerProperties taijiOauthorResource() {
return new ResourceServerProperties();
}

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
List<AuthenticationProvider> authenticationProviderList = new ArrayList<AuthenticationProvider>();
authenticationProviderList.add(customAuthenticationProvider());
AuthenticationManager authenticationManager = new ProviderManager(authenticationProviderList);
return authenticationManager;
}

@Autowired
public TaijiUserDetailServiceImpl userDetailsService;

@Bean
public TaijiAuthenticationProvider customAuthenticationProvider() {
TaijiAuthenticationProvider customAuthenticationProvider = new TaijiAuthenticationProvider();
customAuthenticationProvider.setUserDetailsService(userDetailsService);
return customAuthenticationProvider;
}

@Autowired
private MenuService menuService;
@Autowired
private RoleService roleService;
@Bean
public TaijiSecurityMetadataSource taijiSecurityMetadataSource() {
TaijiSecurityMetadataSource fisMetadataSource = new TaijiSecurityMetadataSource();
// fisMetadataSource.setMenuService(menuService);
fisMetadataSource.setRoleService(roleService);
return fisMetadataSource;
}

@Autowired
private CommunityAccessDecisionManager accessDecisionManager;
@Bean
public CommunityFilterSecurityInterceptor communityfiltersecurityinterceptor() throws Exception {
CommunityFilterSecurityInterceptor taijifiltersecurityinterceptor = new CommunityFilterSecurityInterceptor();
taijifiltersecurityinterceptor.setFisMetadataSource(taijiSecurityMetadataSource());
taijifiltersecurityinterceptor.setAccessDecisionManager(accessDecisionManager);
taijifiltersecurityinterceptor.setAuthenticationManager(authenticationManagerBean());
return taijifiltersecurityinterceptor;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
//     .antMatchers("/").permitAll()
//  .antMatchers("/login").permitAll() //
//  .antMatchers("/image").permitAll() //
//  .antMatchers("/upload/*").permitAll() // for
//  .antMatchers("/common/**").permitAll() // for
//  .antMatchers("/community/**").permitAll()

//     .antMatchers("/").anonymous()
    .antMatchers("/personal/**").authenticated()
    .antMatchers("/notify/**").authenticated()
    .antMatchers("/admin/**").authenticated()
    .antMatchers("/manage/**").authenticated()
    .antMatchers("/**/personal/**").authenticated()
    .antMatchers("/user/**").authenticated()
 .anyRequest()
 .permitAll()
//  .authenticated()
 .and()
 .logout()
 .logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
 .addLogoutHandler(customSsoLogoutHandler)
 .deleteCookies("JSESSIONID").invalidateHttpSession(true)
 .and()
 .csrf().disable()
 //.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
 //.and()
 .addFilterBefore(loginFilter(), BasicAuthenticationFilter.class)
 .addFilterAfter(communityfiltersecurityinterceptor(), FilterSecurityInterceptor.class);///TaijiSecurity权限控制
}

@Override
public void configure(WebSecurity web) throws Exception {
// 解决静态资源被拦截的问题
web.ignoring().antMatchers("/theme/**")
 .antMatchers("/community/**")
 .antMatchers("/common/**")
 .antMatchers("/upload/*");
web.httpFirewall(allowUrlEncodedSlashHttpFirewall());
}

public OAuth2ClientAuthenticationProcessingFilter loginFilter() throws Exception {
OAuth2ClientAuthenticationProcessingFilter ff = new OAuth2ClientAuthenticationProcessingFilter("/login");
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(taiji(),oauth2ClientContext);
ff.setRestTemplate(restTemplate);
UserInfoTokenServices tokenServices = new UserInfoTokenServices(taijiOauthorResource().getUserInfoUri(), taiji().getClientId());
tokenServices.setRestTemplate(restTemplate);
ff.setTokenServices(tokenServices);
ff.setAuthenticationSuccessHandler(customSuccessHandler());
   ff.setAuthenticationFailureHandler(customFailureHandler());
return ff;
}
}

授权成功回调类,认证成功用户落地,如下:


public class CommunitySuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

protected final Log logger = LogFactory.getLog(this.getClass());

private RequestCache requestCache = new HttpSessionRequestCache();
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Inject
AuthenticationManager authenticationManager;
@Value("${security.oauth2.default.roleName}")
private String defaultRole;
@Inject
TaijiOperationLogService taijiOperationLogService;

@Inject
CommunityConfiguration communityConfiguration;

@Inject
private ObjectMapper objectMapper;

@ScoreRule(code="login_score")
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
 Authentication authentication) throws ServletException, IOException {
// 存放authentication到SecurityContextHolder
SecurityContextHolder.getContext().setAuthentication(authentication);
HttpSession session = request.getSession(true);
// 在session中存放security context,方便同一个session中控制用户的其他操作
session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
OAuth2Authentication oauth2Authentication = (OAuth2Authentication) authentication;
Object details = oauth2Authentication.getUserAuthentication().getDetails();
UserDto user = saveUser((Map) details);//用户落地
Collection<GrantedAuthority> obtionedGrantedAuthorities = obtionGrantedAuthorities(user);
UsernamePasswordAuthenticationToken newToken = new UsernamePasswordAuthenticationToken(
 new User(user.getLoginName(), "", true, true, true, true, obtionedGrantedAuthorities),
 authentication.getCredentials(), obtionedGrantedAuthorities);

newToken.setDetails(details);
Object oath2details=oauth2Authentication.getDetails();
oauth2Authentication = new OAuth2Authentication(oauth2Authentication.getOAuth2Request(), newToken);
oauth2Authentication.setDetails(oath2details);
oauth2Authentication.setAuthenticated(true);
SecurityContextHolder.getContext().setAuthentication(oauth2Authentication);

LogUtil.log2database(taijiOperationLogService, request, user.getLoginName(), "user", "", "", "user_login", "登录", "onAuthenticationSuccess","");
session.setAttribute("user", user);
Collection<GrantedAuthority> authorities = (Collection<GrantedAuthority>) authentication.getAuthorities();

SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
 super.onAuthenticationSuccess(request, response, authentication);
 return;
}
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl()
 || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
 requestCache.removeRequest(request, response);
 super.onAuthenticationSuccess(request, response, authentication);
 return;
}
clearAuthenticationAttributes(request);
// Use the DefaultSavedRequest URL
String targetUrl = savedRequest.getRedirectUrl();
// logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);

logger.debug("Redirecting to last savedRequest Url: " + targetUrl);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
// getRedirectStrategy().sendRedirect(request, response, this.getDefaultTargetUrl());
}

public void setRequestCache(RequestCache requestCache) {
this.requestCache = requestCache;
}

//用户落地
private UserDto saveUser(Map userInfo) {
UserDto dto=null;
try {
 String json = objectMapper.writeValueAsString(userInfo);
 dto = objectMapper.readValue(json,UserDto.class);
} catch (JsonProcessingException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
} catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
}

UserDto user=userService.findByLoginName(dto.getLoginName());
if(user!=null) {
 return user;
}
Set<RoleDto> roles= new HashSet<RoleDto>();
RoleDto role = roleService.findByRoleName(defaultRole);
roles.add(role);
dto.setRoles(roles);
List<UserDto> list = new ArrayList<UserDto>();
list.add(dto);
dto.generateTokenForCommunity(communityConfiguration.getControllerSalt());
String id =userService.saveUserWithRole(dto,communityConfiguration.getControllerSalt());
dto.setId(id);
return dto;
}

/**
  * Map转成实体对象
  *
  * @param map  map实体对象包含属性
  * @param clazz 实体对象类型
  * @return
  */
 public static <T> T map2Object(Map<String, Object> map, Class<T> clazz) {
   if (map == null) {
     return null;
   }
   T obj = null;
   try {
     obj = clazz.newInstance();

Field[] fields = obj.getClass().getDeclaredFields();
     for (Field field : fields) {
       int mod = field.getModifiers();
       if (Modifier.isStatic(mod) || Modifier.isFinal(mod)) {
         continue;
       }
       field.setAccessible(true);
       String filedTypeName = field.getType().getName();
       if (filedTypeName.equalsIgnoreCase("java.util.date")) {
         String datetimestamp = String.valueOf(map.get(field.getName()));
         if (datetimestamp.equalsIgnoreCase("null")) {
           field.set(obj, null);
         } else {
           field.set(obj, new Date(Long.parseLong(datetimestamp)));
         }
       } else {
        String v = map.get(field.getName()).toString();
         field.set(obj, map.get(field.getName()));
       }
     }
   } catch (Exception e) {
     e.printStackTrace();
   }
   return obj;
 }

// 取得用户的权限
private Collection<GrantedAuthority> obtionGrantedAuthorities(UserDto users) {
Collection<GrantedAuthority> authSet = new HashSet<GrantedAuthority>();
// 获取用户角色
Set<RoleDto> roles = users.getRoles();
if (null != roles && !roles.isEmpty())
 for (RoleDto role : roles) {
authSet.add(new SimpleGrantedAuthority(role.getId()));
 }
return authSet;
}
}

客户端应用,单点登录方法,如下:


@RequestMapping(value = "/loadToken", method = { RequestMethod.GET })
public void loadToken(Model model,HttpServletResponse response,@RequestParam(value = "clientId", required = false) String clientId) {
String token = "";
RequestAttributes ra = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes sra = (ServletRequestAttributes) ra;
HttpServletRequest request = sra.getRequest();
HttpSession session = request.getSession();
if (session.getAttribute("SPRING_SECURITY_CONTEXT") != null) {
 SecurityContext securityContext = (SecurityContext)session.getAttribute("SPRING_SECURITY_CONTEXT");
 Authentication authentication = securityContext.getAuthentication();
 OAuth2AuthenticationDetails OAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) authentication.getDetails();
 token = OAuth2AuthenticationDetails.getTokenValue();
}
try {
 String url = "http://localhost:9999/rediect?clientId=dev&token="+token;
 response.sendRedirect(url);
} catch (IOException e) {
 e.printStackTrace();
}
}

服务端应用,单点登录方法,如下:


@RequestMapping("/rediect")
public String rediect(HttpServletResponse responsel, String clientId, String token) {
OAuth2Authentication authentication = tokenStore.readAuthentication(token);
if (authentication == null) {
 throw new InvalidTokenException("Invalid access token: " + token);
}
OAuth2Request request = authentication.getOAuth2Request();
Map map = new HashMap();
map.put("code", request.getRequestParameters().get("code"));
map.put("grant_type", request.getRequestParameters().get("grant_type"));
map.put("response_type", request.getRequestParameters().get("response_type"));
//TODO 需要查询一下要跳转的Client_id配置的回调地址
map.put("redirect_uri", "http://127.0.0.1:8888");
map.put("client_id", clientId);
map.put("state", request.getRequestParameters().get("state"));
request = new OAuth2Request(map, clientId, request.getAuthorities(), request.isApproved(), request.getScope(),
 request.getResourceIds(), map.get("redirect_uri").toString(), request.getResponseTypes(),request.getExtensions()); // 模拟用户登录
Authentication t = tokenStore.readAuthentication(token);
OAuth2Authentication auth = new OAuth2Authentication(request, t);
OAuth2AccessToken new_token = defaultTokenServices.createAccessToken(auth);
return "redirect:/user_info?access_token=" + new_token.getValue();
}
@RequestMapping({ "/user_info" })
public void user(String access_token,HttpServletResponse response) {
OAuth2Authentication auth=tokenStore.readAuthentication(access_token);
OAuth2Request request=auth.getOAuth2Request();
 Map<String, String> map = new LinkedHashMap<>();
 map.put("loginName", auth.getUserAuthentication().getName());
 map.put("password", auth.getUserAuthentication().getName());
 map.put("id", auth.getUserAuthentication().getName());
 try {
response.sendRedirect(request.getRedirectUri()+"?name="+auth.getUserAuthentication().getName());
} catch (IOException e) {
e.printStackTrace();
}
}

个人总结

Oauth2的设计相对复杂,需要深入学习多看源码才能了解内部的一些规则,如数据token的存储是用的实体序列化后内容,需要反序列才能在项目是使用,也许是为了安全,但在学习过程需要提前掌握,还有在token的过期时间不能为0,通常来讲过期时间为0代表长期有效,但在Oauth2中则报错,这些坑需要一点点探索。

通过集成Spring Security和Oauth2较大的提供的开发的效率,也提供的代码的灵活性和可用性。但封装的核心类需要大家都了解一下,通读下代码,以便在项目中可随时获取需要的参数。

示例代码

以下是个人的一套代码,供参考。

基于Spring Cloud的微服务框架集成Oauth2的代码示例

Oauth2数据结构,如下:

基于Spring Security的Oauth2授权实现方法基于Spring Security的Oauth2授权实现方法基于Spring Security的Oauth2授权实现方法基于Spring Security的Oauth2授权实现方法基于Spring Security的Oauth2授权实现方法

来源:https://blog.csdn.net/z57354658/article/details/82108263

标签:Spring,Security,Oauth2,授权
0
投稿

猜你喜欢

  • Springboot整合redis实现发布订阅功能介绍步骤

    2021-11-23 16:42:26
  • Spring入门实战之Profile详解

    2021-10-03 19:08:34
  • SpringBoot如何实现定时任务示例详解

    2023-10-11 23:24:42
  • IDEA 快速返回上次查看代码的位置的方法

    2022-10-23 01:49:59
  • opencv3/C++ FLANN特征匹配方式

    2021-08-19 10:17:47
  • Eclipse 开发java 出现Failed to create the Java Virtual Machine错误解决办法

    2021-12-24 10:35:50
  • Elasticsearch配置文件示例示范

    2021-11-05 22:59:31
  • SpringBoot整合Security安全框架实现控制权限

    2022-10-03 14:37:15
  • Android简单实现一个颜色渐变的ProgressBar的方法

    2023-07-23 00:30:12
  • C#控件闪烁的解决方法

    2023-02-27 07:30:41
  • 浅析java中Integer传参方式的问题

    2023-10-27 03:35:43
  • SpringBoot+Nacos+Kafka微服务流编排的简单实现

    2023-03-21 10:34:53
  • @NonNull导致无法序列化的问题及解决

    2021-07-21 23:07:33
  • Java Base64解码错误及解决方法

    2023-02-09 03:36:35
  • 论Java Web应用中调优线程池的重要性

    2023-06-13 04:56:27
  • java实现死锁的示例代码

    2023-10-12 18:12:40
  • java之CSV大批量数据入库的实现

    2021-06-30 15:53:49
  • java基础的详细了解第四天

    2022-11-25 07:24:51
  • Spring Boot 2.X快速整合jpa过程解析

    2021-09-22 15:27:29
  • Java中的Runnable,Callable,Future,FutureTask的比较

    2021-12-08 13:19:22
  • asp之家 软件编程 m.aspxhome.com