开源软件名称:justAuth-spring-security-starter
开源软件地址:https://gitee.com/pcore/just-auth-spring-security-starter
开源软件介绍:
Spring security 集成 JustAuth 实现第三方授权登录脚手架:一、特性 Spring security 集成 JustAuth 实现第三方授权登录: 此项目从 用户管理脚手架(UMS):https://github.com/ZeroOrInfinity/UMS | https://gitee.com/pcore/UMS) 项目中分离. - 支持所有 justAuth 支持的第三方登录,登录后自动注册 或 绑定 或 创建临时用户(TemporaryUser).
- 支持定时刷新 accessToken 分布式定时任务,
- 支持第三方授权登录的用户信息表与 token 信息表的 redis 缓存功能.
- 支持第三方绑定与解绑及查询接口.
- 支持一键登录.
登录流程图微信群:UMS 添加微信(z56133)备注(UMS)
二、maven :<dependency> <groupId>top.dcenter</groupId> <artifactId>justAuth-spring-security-starter</artifactId> <version>latest</version></dependency>
三、使用说明:1. 引入依赖 <dependency> <groupId>top.dcenter</groupId> <artifactId>justAuth-spring-security-starter</artifactId> <version>latest</version></dependency> 2. 必须实现的接口 3. 必须添加 Auth2AutoConfigurer 到 HttpSecurity @Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private Auth2AutoConfigurer auth2AutoConfigurer; @Autowired private Auth2Properties auth2Properties; @Override protected void configure(HttpSecurity http) throws Exception { // ========= start: 使用 justAuth-spring-security-starter 必须步骤 ========= // 添加 Auth2AutoConfigurer 使 OAuth2(justAuth) login 生效. http.apply(this.auth2AutoConfigurer); http.csrf().disable(); // 放行第三方登录入口地址与第三方登录回调地址 // @formatter:off http.authorizeRequests() .antMatchers(HttpMethod.GET, auth2Properties.getRedirectUrlPrefix() + "/*", auth2Properties.getAuthLoginUrlPrefix() + "/*") .permitAll(); // @formatter:on // ========= end: 使用 justAuth-spring-security-starter 必须步骤 ========= }} 4. 属性配置 - justAuth-spring-security-starter 大部分功能实现都是通过配置文件设置属性来完成的, 详细属性配置请查看
五、属性配置列表 .
四、接口说明:UmsUserDetailsService: 必须实现 Auth2StateCoder: 用户需要时实现 , 对第三方授权登录流程中的 state 进行自定义编解码. 可以传递必要的信息,如: 第三方登录成功的跳转地址等 注意此接口的两个方法必须同时实现对应的编解码逻辑, 实现此接口后注入 IOC 容器即可, 如有前端向后端获取 authorizeUrl时向后端传递额外参数 且用作注册时的信息, 需配合 UmsUserDetailsService.registerUser(AuthUser, String, String, String) 方法实现. Auth2UserService: 获取第三方用户信息的接口, 一般不需要用户实现, 除非想自定义获取第三方用户信息的逻辑, 实现此接口注入 IOC 容器即可替代. ConnectionService: 第三方授权登录用户的注册, 绑定, 更新第三方用户信息与 accessToken 信息的接口, 一般不需要用户实现.除非想自定义获取第三方用户信息的逻辑, 实现此接口注入 IOC 容器即可替代. 取消 OAuth2 的内置数据库说明一. 同时取消第三方登录的 user_connection 与 auth_token 表1. 属性配置ums: oauth: # 是否支持内置的第三方登录用户表(user_connection) 和 auth_token 表. 默认: true. # 注意: 如果为 false, 则必须重新实现 ConnectionService 接口. enable-user-connection-and-auth-token-table: false 2. 必须重新实现 top.dcenter.ums.security.core.oauth.signup.ConnectionService 接口二. 取消第三方登录 auth_token 表1. 属性配置ums: oauth: # 是否支持内置的第三方登录 token 表(auth_token). 默认: true. enable-auth-token-table: false
自定义 OAuth2 Login 扩展接口: 内置两个自定义 providerId(ums.oauth.customize 与 ums.oauth.gitlabPrivate) AuthGitlabPrivateSource: 抽象类, 实现此自定义的 AuthGitlabPrivateSource 且注入 ioc 容器的同时, 必须实现 AuthCustomizeRequest , 会自动集成进 OAuth2 Login 逻辑流程中, 只需要像 JustAuth 默认实现的第三方登录一样, 配置相应的属性(ums.oauth.gitlabPrivate.[clientId|clientSecret]等属性)即可. AuthCustomizeSource: 抽象类, 实现此自定义的 AuthCustomizeSource 且注入 ioc 容器的同时, 必须实现 AuthCustomizeRequest , 会自动集成进 OAuth2 Login 逻辑流程中, 只需要像 JustAuth 默认实现的第三方登录一样, 配置相应的属性(ums.oauth.customize.[clientId|clientSecret]等属性)即可. AuthCustomizeRequest: 抽象类, 实现此自定义的 AuthCustomizeRequest 同时, 必须实现 AuthCustomizeSource 或 AuthGitlabPrivateSource 且注入 ioc 容器, 会自动集成进 OAuth2 Login 逻辑流程中, 只需要像 JustAuth 默认实现的第三方登录一样, 配置相应的属性(ums.oauth.customize.[clientId|clientSecret]等属性)即可.
OneClickLoginService: 一键登录功能开启时, 必须实现 此接口, 根据 accessToken 从服务商获取用户手机号.
五、Jackson 序列化与反序列化 // 示例Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper objectMapper = new ObjectMapper();// Auth2Jackson2Module 为此项目实现的反序列化配置 objectMapper.registerModules(new CoreJackson2Module(), new WebJackson2Module(), new Auth2Jackson2Module());jackson2JsonRedisSerializer.setObjectMapper(om);
六、快速开始(Quick Start) :1. 添加依赖:<dependency> <groupId>top.dcenter</groupId> <artifactId>justAuth-spring-security-starter</artifactId> <version>latest</version></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.3.4.RELEASE</version></dependency><!-- mysql --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.21</version></dependency><!-- 第三方授权登录默认会按照下面的优先级自行寻找一种 HTTP 工具依赖,java 11 HttpClient -> OkHttp3 -> apache HttpClient -> hutool-http 示例使用 apache HttpClient . 注意: 如果是 JDK11 则不需要此依赖--><dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.12</version></dependency> 依赖说明: 如果是 JDK-1.8 环境, 任选一种 HTTP 工具依赖,项目内如果已有,请忽略。另外需要特别注意,如果项目中已经引入了低版本的依赖,请先排除低版本以后来,引入高版本或者最新版本的依赖<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-http</artifactId> <version>5.2.5</version></dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.12</version></dependency> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.4.1</version></dependency> 2. config:server: port: 9090spring: profiles: active: dev # mysql datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/ums?useSSL=false&useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai username: root password: 123456 # session 简单配置 session: # session 存储模式设置, 要导入相应的 spring-session 类的依赖, 默认为 none, 分布式应用把 session 放入 redis 等中间件 store-type: none # session 过期时间 timeout: PT300s# ums coreums: # ================ 第三方授权登录相关配置 ================ oauth: # 是否支持第三方授权登录功能, 默认: true enabled: true # 抑制反射警告, 支持 JDK11, 默认: false , 在确认 WARNING: An illegal reflective access operation has occurred 安全后, 可以打开此设置, 可以抑制反射警告. suppress-reflect-warning: true # 第三方服务商: providerId, 支持所有 JustAuth 支持的第三方授权登录, 目前有 32 家第三方授权登录 github: # 根据是否有设置 clientId 来动态加载相应 JustAuth 的 AuthXxxRequest client-id: 4d4ee00e82f669f2ea8d client-secret: 953ddbe871a08d6924053531e89ecc01d87195a8 gitee: client-id: dcc38c801ee88f43cfc1d5c52ec579751c12610c37b87428331bd6694056648e client-secret: e60a110a2f6e7c930c2d416f802bec6061e19bfa0ceb0df9f6b182b05d8f5a58 # 第三方登录授权登录 url 前缀, 不包含 ServletContextPath,默认为 /auth2/authorization. auth-login-url-prefix: /auth2/authorization # 第三方登录回调处理 url 前缀 ,也就是 RedirectUrl 的前缀, 不包含 ServletContextPath,默认为 /auth2/login. redirect-url-prefix: /auth2/login # 第三方登录回调的域名, 例如:http://localhost:9090 默认为 "http://127.0.0.1", # redirectUrl 直接由 {domain}/{servletContextPath}/{redirectUrlPrefix}/{providerId}(ums.oauth.[qq/gitee/weibo])组成 domain: http://localhost:9090 # 第三方授权登录成功后的默认权限, 多个权限用逗号分开, 默认为: "ROLE_USER" default-authorities: ROLE_USER # 用于 JustAuth 的代理(HttpClient)设置 proxy: # 用于国内代理(HttpClient)超时, 默认 PT3S timeout: PT3S # 用于国外网站代理(HttpClient)超时, 默认 PT15S foreign-timeout: PT150S ---spring: profiles: dev mvc: throw-exception-if-no-handler-found: true#debug: trueserver: port: 9090 servlet: context-path: /demo 3. 必须实现 UmsUserDetailsService 接口:UserDetailsServiceImpl.javaimport me.zhyd.oauth.model.AuthUser;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.AuthorityUtils;import org.springframework.security.core.userdetails.User;import org.springframework.security.core.userdetails.UserCache;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.stereotype.Service;import top.dcenter.ums.security.core.oauth.enums.ErrorCodeEnum;import top.dcenter.ums.security.core.oauth.exception.RegisterUserFailureException;import top.dcenter.ums.security.core.oauth.exception.UserNotExistException;import top.dcenter.ums.security.core.oauth.service.UmsUserDetailsService;import java.util.List;/** * 用户密码与手机短信登录与注册服务:<br><br> * 1. 用于第三方登录与手机短信登录逻辑。<br><br> * 2. 用于用户密码登录逻辑。<br><br> * 3. 用户注册逻辑。<br><br> * @author YongWu zheng * @version V1.0 Created by 2020/9/20 11:06 */@Servicepublic class UserDetailsServiceImpl implements UmsUserDetailsService { private final Logger log = LoggerFactory.getLogger(this.getClass()); @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") @Autowired(required = false) private UserCache userCache; /** * 用于密码加解密 */ @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection") @Autowired private PasswordEncoder passwordEncoder; @SuppressWarnings("AlibabaUndefineMagicConstant") @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { try { // 从缓存中查询用户信息: // 从缓存中查询用户信息 if (this.userCache != null) { UserDetails userDetails = this.userCache.getUserFromCache(username); if (userDetails != null) { return userDetails; } } // 根据用户名获取用户信息 // 获取用户信息逻辑。。。 // ... // 示例:只是从用户登录日志表中提取的信息, log.info("Demo ======>: 登录用户名:{}, 登录成功", username); return new User(username, passwordEncoder.encode("admin"), true, true, true, true, AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_VISIT, ROLE_USER")); } catch (Exception e) { String msg = String.format("Demo ======>: 登录用户名:%s, 登录失败: %s", username, e.getMessage()); log.error(msg); throw new UserNotExistException(ErrorCodeEnum.QUERY_USER_INFO_ERROR, e, username); } } @Override public UserDetails registerUser(AuthUser authUser, String username, String defaultAuthority, String decodeState) throws RegisterUserFailureException { // 第三方授权登录不需要密码, 这里随便设置的, 生成环境按自己的逻辑 String encodedPassword = passwordEncoder.encode(authUser.getUuid()); // 这里的 decodeState 可以根据自己实现的 top.dcenter.ums.security.core.oauth.service.Auth2StateCoder 接口的逻辑来传递必要的参数. // 比如: 第三方登录成功后的跳转地址 final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); // 假设 decodeState 就是 redirectUrl, 我们直接把 redirectUrl 设置到 request 上 // 后续经过成功处理器时直接从 requestAttributes.getAttribute("redirectUrl", RequestAttributes.SCOPE_REQUEST) 获取并跳转 requestAttributes.setAttribute("redirectUrl", decodeState, RequestAttributes.SCOPE_REQUEST); // 当然 decodeState 也可以传递从前端传到后端的用户信息, 注册到本地用户 List<GrantedAuthority> grantedAuthorities = AuthorityUtils.commaSeparatedStringToAuthorityList(defaultAuthority); // ... 用户注册逻辑 log.info("Demo ======>: 用户名:{}, 注册成功", username); // @formatter:off UserDetails user = User.builder() .username(username) .password(encodedPassword) .disabled(false) .accountExpired(false) .accountLocked(false) .credentialsExpired(false) .authorities(grantedAuthorities) .build(); // @formatter:off // 把用户信息存入缓存 if (userCache != null) { userCache.putUserInCache(user); } return user; } @Override public UserDetails loadUserByUserId(String userId) throws UsernameNotFoundException { UserDetails userDetails = loadUserByUsername(userId); User.withUserDetails(userDetails); return User.withUserDetails(userDetails).build(); } /** * {@link #existedByUsernames(String...)} usernames 生成规则. * 如需自定义重新实现此逻辑 * @param authUser 第三方用户信息 * @return 返回一个 username 数组 */ @Override public String[] generateUsernames(AuthUser authUser) { return new String[]{ authUser.getUsername(), // providerId = authUser.getSource() authUser.getUsername() + "_" + authUser.getSource(), // providerUserId = authUser.getUuid() authUser.getUsername() + "_" + authUser.getSource() + "_" + authUser.getUuid() }; } @Override public List<Boolean> existedByUsernames(String... usernames) throws UsernameNotFoundException { // ... 在本地账户上查询 userIds 是否已被使用 List<Boolean> list = new ArrayList<>(); list.add(true); list.add(false); list.add(false); return list; }} 4. 必须添加 top.dcenter.ums.security.core.oauth.config.Auth2AutoConfigurer 到 HttpSecurityWebSecurityConfig.javaimport org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.HttpMethod;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.factory.PasswordEncoderFactories;import org.springframework.security.crypto.password.PasswordEncoder;import top.dcenter.ums.security.core.oauth.config.Auth2AutoConfigurer;import top.dcenter.ums.security.core.oauth.properties.Auth2Properties;/** * web security config * @author YongWu zheng * @version V2.0 Created by 2020/10/18 22:39 */@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private Auth2AutoConfigurer auth2AutoConfigurer; @Autowired private Auth2Properties auth2Properties; @Bean public PasswordEncoder passwordEncoder() { /* 默认为 BCryptPasswordEncoder 的实现了添加随机 salt 算法,并且能从hash后的字符串中获取 salt 进行原始密码与hash后的密码的对比 支持格式: {bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG {noop}password {pbkdf2}5d923b44a6d129f3ddf3e3c8d29412723dcbde72445e8ef6bf3b508fbf17fa4ed4d6b99ca763d8dc {scrypt}$e0801$8bWJaSu2IKSn9Z9kM+TPXfOc/9bdYSrN1oD9qfVThWEwdRTnO7re7Ei+fUZRJ68k9lTyuTeUp4of4g24hHnazw==$OAOec05+bXxvuu/1qZ6NUR+xQYvYv7BeL1QxwRpY5Pc= {sha256}97cde38028ad898ebc02e690819fa220e88c62e0699403e94fff291cfffaf8410849f27605abcbc0 */ return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception
|
请发表评论