分享程序网
首页
  • java
微服务
微前端
环境搭建
数据库
设计模式
算法
软件
解决问题
链接
首页
  • java
微服务
微前端
环境搭建
数据库
设计模式
算法
软件
解决问题
链接
  • 微服务

    • 介绍
  • 微服务搭建

    • 初步搭建
  • 服务发现

    • Eureka
    • nacos
  • 网关

    • zuul
    • 网关配置
    • 过滤器
    • 动态加载
  • 认证(Oauth)

    • 初始化项目
    • Oauth2配置
    • 对外接口
  • 通用服务

    • 通用功能父模块
    • redis
  • 任务调度

    • 任务调度服务
    • xxl-job示例
  • 业务服务

    • 业务设计

Oauth2配置

在对Oauth2配置之前需要先熟悉三个适配器

  • WebSecurityConfigurerAdapter
  • AuthorizationServerConfigurerAdapter
  • ResourceServerConfigurerAdapter

说一下以上三个适配器的区别

WebSecurityConfigurerAdapter是在spring-security-config包下的,AuthorizationServerConfigurerAdapter和ResourceServerConfigurerAdapter是在spring-security-oauth包下的,这样WebSecurityConfigurerAdapter适配器就与另外两个有了本质的区别。WebSecurityConfigurerAdapter是对当前项目的http配置,ResourceServerConfigurerAdapter是对oauth2的http配置,且WebSecurityConfigurerAdapter执行顺序要优先于ResourceServerConfigurerAdapter。AuthorizationServerConfigurerAdapter主要是对token认证的配置。

WebSecurityConfigurerAdapter

WebSecurityConfigurerAdapter是对当前系统进行鉴权,包含且不仅限于跨域,表单提交,session处理等。

是Spring Security的标准配置入口。在 此项目中,主要用来配置 PasswordEncoder、用户自定义查询和HttpSecurity安全规则。

在com.shareprog.auth.configuration.security包下创建自定义适配器,代码如下:

package com.shareprog.auth.configuration.security;

import java.util.Arrays;
import java.util.Collections;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	
	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	public void configure(WebSecurity web) throws Exception {
        // 允许静态资源访问,可以在这里写单点登录页面,未使用
		web.ignoring().antMatchers("templates/**");
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(userDetailsService)
			.passwordEncoder(passwordEncoder());
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
				.antMatchers("/oauth/user","/oauth/logout").permitAll()
				.anyRequest().authenticated()
				//登录时会传一个csrf进行验证,如果没有则不会进行跳转,所以在这里需要关掉该功能
			.and()
			.csrf().disable()	//禁用csrf
				.cors()
			.and()
			.httpBasic()
			;
	}
	
	/**
	 * 解决Cors跨域问题
	 * @return
	 */
	@Bean
	public CorsConfigurationSource corsConfigurationSource() {
		final CorsConfiguration configuration = new CorsConfiguration();
		// 预检请求的有效期,单位为秒
		configuration.setMaxAge(3600L);
		//允许跨域访问的域名
		configuration.setAllowedOrigins(Collections.singletonList("*"));
		//允许的请求方法
		configuration.setAllowedMethods(Arrays.asList("GET", "POST", "HEAD", "DELETE", "PATCH", "PUT", "OPTION"));
		// 是否发送cookie信息
		configuration.setAllowCredentials(false);
		//允许的请求头
		configuration.setAllowedHeaders(Collections.singletonList("*"));
		//暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
		configuration.addExposedHeader("Authorization");
		final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
		source.registerCorsConfiguration("/**", configuration);
		return source;
	}
	
	/**
	 * 加密方式
	 * <p>Title: passwordEncoder</p>  
	 * <p>Description: </p>  
	 * @return
	 */
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
	/**
	 * 认证管理对象
	 */
	@Bean
	@Override
	public AuthenticationManager authenticationManagerBean() throws Exception {
		return super.authenticationManagerBean();
	}
}

上面所有的方法中,还必须自定义实现UserService 的用户查询方法(loadUserByUsername),在此之前,需要先创建一个用户表。为了减少代码开发量,可以看到loadUserByUsername方法返回的是UserDetails类,该类spring-security-core已经提供了一个User实现类(org.springframework.security.core.userdetails.User),该类属性如下:

public class User implements UserDetails, CredentialsContainer {
    // 用户密码
	private String password;
    // 用户名
	private final String username;
    // 权限集合
	private final Set<GrantedAuthority> authorities;
    // 账户是否未过期
	private final boolean accountNonExpired;
    // 账户是否解锁
	private final boolean accountNonLocked;
    // 账户(凭据)密码是否未过期
	private final boolean credentialsNonExpired;
    // 账户是否已启用
	private final boolean enabled;
	...
}

一般来说,我们对用户只有是否禁用/启用,这个时候账户过期,解锁,密码过期都是不用的,但是boolean默认值是false。这几个值要么通过数据库反序列化赋值,要么使用一个子类继承重写get方法,这里为了方便,直接通过User提供的建造者模式直接返回一个新的对象。

首先在pom.xml文件中添加JDBC依赖:

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

<!--mysql-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

application.yml文件中也需要加入数据库配置,加入后结果如下:

spring: 
  datasource: 
    url: jdbc:mysql://127.0.0.1:3306/shareprog?userUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  security:
    user:
      name: auth
      password: auth

这时候必须现有一个用户表,数据库Mysql表DDL如下:

DROP TABLE IF EXISTS `user`;
CREATE TABLE `users` (
  `username` varchar(64) NOT NULL COMMENT '用户名',
  `password` varchar(255) NOT NULL COMMENT '密码',
  `enabled` tinyint(1) DEFAULT '1' COMMENT '是否启用',
  PRIMARY KEY (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表' ROW_FORMAT = Compact;

ALTER TABLE users ADD UNIQUE (username);

关于表的设计可以参考UserDetailsService的实现类:org.springframework.security.provisioning.JdbcUserDetailsManager,内含大量的对users表和groups表的增删改查。在通常的OA系统中,groups代表的应该是部门。在此我们使用自定义的用户-角色-权限:

以下DDL仅供参考使用:

DROP TABLE IF EXISTS `users_roles`;
CREATE TABLE `users_roles` (
  `username` varchar(64) NOT NULL COMMENT '用户名',
  `role` varchar(64) NOT NULL COMMENT '角色',
  PRIMARY KEY (`username`,`role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户和角色关联表';

DROP TABLE IF EXISTS `roles`;
CREATE TABLE `roles` (
  `role` varchar(64) NOT NULL COMMENT '角色',
  `role_name` varchar(255) NOT NULL COMMENT '角色名称',
  PRIMARY KEY (`role`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色表';

DROP TABLE IF EXISTS `roles_authorities`;
CREATE TABLE `roles_authorities` (
  `role` varchar(64) NOT NULL COMMENT '角色',
  `authority` int(64) NOT NULL COMMENT '权限',
  PRIMARY KEY (`role`,`authority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色权限表';

DROP TABLE IF EXISTS `authorities`;
CREATE TABLE `authorities` (
  `authority` varchar(64) NOT NULL COMMENT '权限',
  `authority_name` varchar(255) NOT NULL COMMENT '权限名称',
  PRIMARY KEY (`authority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='权限表';

DROP TABLE IF EXISTS `users_authorities`;
CREATE TABLE `users_authorities`  (
  `username` varchar(128) NOT NULL COMMENT '用户名',
  `authority` varchar(128) NOT NULL COMMENT '权限',
  PRIMARY KEY (`username`, `authority`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '用户权限表';

在com.shareprog.auth.service包下创建自定义用户权限查询,代码如下:

package com.shareprog.auth.service;

import java.util.List;
import java.util.stream.Collectors;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

import lombok.AllArgsConstructor;

/**
 * @Title 自定义权限用户查询
 * @author cl
 * @date 2019年12月11日 下午5:32:23
 * @version 1.0
 */
@Service
@AllArgsConstructor
public class CustomUserService implements UserDetailsService {
	
	private static final String FIND_BY_USEENAME_STATEMENT = "SELECT username, password, enabled FROM user WHERE username = ?";
	private static final String LIST_ALL_AUTHORITY_STATEMENT = "SELECT `authority` FROM authorities";
	private static final String LIST_AUTHORITY_BY_USER_STATEMENT = "SELECT au.`authority` FROM users u"
			+ " left join users_roles ur on u.username = ur.username "
			+ " left join roles r on r.role = ur.role"
			+ " left join roles_authorities ra on ra.role = r.role"
			+ " left join authorities au on au.authority = ra.authority";
	
	private final JdbcTemplate jdbcTemplate;
	
	@Override
	public UserDetails loadUserByUsername(String username) {
		Map<String, Object> userMap = jdbcTemplate.queryForMap(FIND_BY_USEENAME_STATEMENT, username);
		List<String> authorities;
		if ("admin".equals(username)) {
			authorities = jdbcTemplate.queryForList(LIST_ALL_AUTHORITY_STATEMENT, String.class);
		} else {
			authorities = jdbcTemplate.queryForList(LIST_AUTHORITY_BY_USER_STATEMENT, String.class, username);
		}
		authorities.add("isLogin");
		return User.builder()
					.username((String) userMap.get("username"))
					.password((String) userMap.get("password"))
					.disabled(!(boolean)userMap.get("enabled"))
					.authorities(authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()))
					.build();
	}
}

ResourceServerConfigurerAdapter

这是资源服务器配置,主要是基于oauth2对http安全配置和资源安全配置相关。

资源服务器的职责是对来自OAuth客户端的access_token进行鉴权。一个资源服务器包含多个接口,一部分接口作为资源服务器的资源提供给OAuth的客户端访问,另一部分接口不由资源服务器管理。由资源服务器管理的接口安全性配置在此类中,其余接口的安全性配置在WebSecurityConfig类中。当请求中包含Oauth2的access_token时,Spring Security会根据资源服务器配置进行过滤。EnableResourceServer会创建一个WebSecurityConfigurerAdapter,执行顺序(Order)是3。在WebSecurityConfig之前运行,优先级更高。

ResourceServerConfigurerAdapter提供了以下两方面的自定义配置。

  1. HttpSecurity:Spring Security的安全配置类,注意此处配置的URL与过滤器映射关系的优先级 要高于SecurityConfiguration类。
  2. ResourceServerSecurityConfigurer:配置ResourceServerTokenServices、resourceId等。

ResourceServerTokenServices的配置很关键。OAuth的资源服务器主要负责对传递进来的access_token 进行验证,验证通过后才允许 OAuth 客户端访问资源端点。在实际应用中,由于授权服 务器和资源服务器的系统架构不同,所以具体的验证方式也不同,常用的验证方式有以下三种。

  1. 当授权服务器与资源服务器在同一个应用中时,使用默认的DefaultTokenServices在服务器内部进行验证。
  2. 当授权服务器和资源服务器是分离的两个应用,且access_token类型为Opaque时,使用RemoteTokenServices资源服务器远程调用授权服务器进行验证。
  3. 当授权服务器和资源服务器是分离的两个应用,且access_token类型为JWT时,一般使用DefaultTokenServices 资源服务器远程调用授权服务器进行验证。注意,需要将DefaultTokenServices的tokenStore属性设置为JwtTokenStore。

另外,需要配置属性resourceId为“sso-resource”,代替默认值“oauth2-resource”。

package com.shareprog.auth.configuration.security;

import javax.servlet.http.HttpServletResponse;

import org.springframework.context.annotation.Configuration;
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.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;

@EnableResourceServer
@Configuration
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

	public static final String RESOURCE_ID = "sso-resource";
	
	@Override
	public void configure(HttpSecurity http) throws Exception {
		http
			.csrf().disable()
			.exceptionHandling()
				.authenticationEntryPoint(
						(request, response, authException) -> 
						response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
			.and()
			.authorizeRequests()
				.antMatchers("/oauth/**").permitAll()
				.anyRequest().permitAll()
			.and()
			.httpBasic();
	}

	@Override
	public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
		resources.resourceId(RESOURCE_ID)
            		//.stateless(false)
            		;
	}

}

AuthorizationServerConfigurerAdapter

授权服务器配置

package com.shareprog.auth.configuration.security;

import javax.sql.DataSource;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;

import com.shareprog.auth.configuration.security.authentication.CustomUserAuthenticationConverter;
import com.shareprog.auth.configuration.security.handler.CustomWebResponseExceptionTranslator;
import com.shareprog.auth.configuration.security.service.CustomJdbcClientDetailsService;

import lombok.RequiredArgsConstructor;

@EnableAuthorizationServer
@Configuration
@RequiredArgsConstructor
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

	private final DataSource dataSource;
	private final AuthenticationManager authenticationManager;
	private final UserDetailsService userDetailsService;
	private final RedisConnectionFactory connectionFactory;

	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		clients.withClientDetails(clientDetailsService());
	}

	@Override
	public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
		security.allowFormAuthenticationForClients()
				.tokenKeyAccess("permitAll()")
				.checkTokenAccess("permitAll()");//设置为isAuthenticated()则校验token需要认证
	}

	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		DefaultAccessTokenConverter defaultAccessTokenConverter=new DefaultAccessTokenConverter();
		defaultAccessTokenConverter.setUserTokenConverter(new CustomUserAuthenticationConverter());
		endpoints
				.authenticationManager(authenticationManager)
				.userDetailsService(userDetailsService)
				.tokenStore(jdbcTokenStore())
				.accessTokenConverter(defaultAccessTokenConverter)
				.exceptionTranslator(new CustomWebResponseExceptionTranslator())
				;
	}

	public TokenStore redisTokenStore() {
		return new RedisTokenStore(connectionFactory);
	}
	
	public JdbcTokenStore jdbcTokenStore() {
		return new JdbcTokenStore(dataSource);
	}
	
	private ClientDetailsService clientDetailsService() {
		return new CustomJdbcClientDetailsService(dataSource);
	}
	
	@Bean
	public TokenEnhancer accessTokenConverter() {
		final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
		// 配置自定义转换器
		DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
		tokenConverter.setUserTokenConverter(new CustomUserAuthenticationConverter());
		converter.setAccessTokenConverter(tokenConverter);
		return converter;
	}
}

授权服务器主要是对token进行配置

AuthorizationServerSecurityConfigurer主要配置授权服务器的安全,主要就是运行所有token校验接口是可以访问的。ClientDetailsServiceConfigurer主要配置授权服务器的客户端clients信息。一般来说可以通过inMemory方式设置client信息,百度上大部分初始教程都是以此种方式的。如果想要做动态配置的话,最好是使用数据库配置,可以使用非关系型数据库redis或者关系型数据库MySQL。

如果使用redis,需要在pom.xml文件添加redis依赖:

	<!-- redis 依赖 -->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-redis</artifactId>
	</dependency>

这里使用MySQL作为示例。

package com.shareprog.auth.configuration.security.service;

import javax.sql.DataSource;

import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;

/**
 * @ClassName: CustomJdbcClientDetailsService
 * @Description: 自定义客户端信息表,可以在构造函数中使用
 * 		setSelectClientDetailsSql方法重写查询sql语句
 * @author cl
 * @date 2021年1月19日
 */
public class CustomJdbcClientDetailsService extends JdbcClientDetailsService {

	public CustomJdbcClientDetailsService(DataSource dataSource) {
		super(dataSource);
	}

}

这里并没有进行任何操作,只是继承了JdbcClientDetailsService的方法,事实上直接使用JdbcClientDetailsService也是可以的。但是一般而言,我们需要自定义一些数据库数据之类的操作,这里为了方便,创建的数据库和JdbcClientDetailsService提供DML保持一致,所以不需要进行任何重写。

DDL如下:

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (
  `client_id` varchar(64) CHARACTER SET utf16 COLLATE utf16_general_ci NOT NULL COMMENT '客户端ID',
  `client_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '客户端名称',
  `client_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '客户端密匙',
  `resource_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '资源id集合',
  `web_server_redirect_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '重定向URL',
  `scope` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '权限范围,可选值包括read,write',
  `authorized_grant_types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '可选值包括authorization_code,password,refresh_token,implicit,client_credentials',
  `authorities` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '指定客户端所拥有的Spring Security的权限值',
  `access_token_validity` int(11) NULL DEFAULT 43200 COMMENT 'token的有效时间值',
  `refresh_token_validity` int(11) NULL DEFAULT 2592000 COMMENT 'refresh_token的有效时间值',
  `additional_information` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
  `autoapprove` tinyint(1) NULL DEFAULT 1 COMMENT '是否信任',
  `create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建时间',
  PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for oauth_access_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token` (
  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `token_id` varchar(255) DEFAULT NULL,
  `token` blob,
  `authentication_id` varchar(255) DEFAULT NULL,
  `user_name` varchar(255) DEFAULT NULL,
  `client_id` varchar(255) DEFAULT NULL,
  `authentication` blob,
  `refresh_token` varchar(255) DEFAULT NULL,
  UNIQUE KEY `authentication_id_index` (`authentication_id`) USING BTREE,
  KEY `token_id_index` (`token_id`),
  KEY `user_name_index` (`user_name`),
  KEY `client_id_index` (`client_id`),
  KEY `refresh_token_index` (`refresh_token`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token` (
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `token_id` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
  `token` blob,
  `authentication` blob,
  KEY `token_id_index` (`token_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

事实上,我们使用oauth2的配置,返回的结果是固定的,有时候我们可能需要通过token需要获取用户的信息,例如昵称、性别、年龄之类的,所以我们还需要自定义返回结果,这里自定义返回一个display_name参数。

这里对使用lang3的工具类进行空的判定,加入依赖:

		<!--lang3工具包-->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
		</dependency>
package com.shareprog.auth.configuration.security.authentication;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;

/**
 * @ClassName: CustomUserAuthenticationConverter
 * @Description: 自定义用户信息返回结果
 * @author cl
 * @date 2021年1月19日
 */
public class CustomUserAuthenticationConverter extends DefaultUserAuthenticationConverter {

	@Override
	public Map<String, ?> convertUserAuthentication(Authentication authentication) {
		Map<String, Object> response = new LinkedHashMap<>();
		response.put(USERNAME, authentication.getName());
		Object principal = authentication.getPrincipal();
		if (principal instanceof User) {
			User user = (User) principal;
			if (StringUtils.isNotEmpty(user.getUsername())) {
				response.put("display_name", user.getUsername());
			}
		}
		if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
			response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
		}
		return response;
	}
}

有时候在oauth2中,我们需要自定义认证失败返回的错误码,例如400,401,402等待,代码如下:

package com.shareprog.auth.configuration.security.handler;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;

import lombok.extern.slf4j.Slf4j;

/**
 * 自定义错误信息响应结果
 *
 * @author cl
 */
@Slf4j
public class CustomWebResponseExceptionTranslator implements WebResponseExceptionTranslator<OAuth2Exception> {

	@Override
	public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
		log.error("响应错误信息:", e);
		return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
	}

}
Last Updated:
Contributors: clcheng
Prev
初始化项目
Next
对外接口