• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

    justAuth-spring-security-starter: spring security 集成 JustAuth 实现第三方授权登 ...

    原作者: [db:作者] 来自: 网络 收藏 邀请

    开源软件名称:

    justAuth-spring-security-starter

    开源软件地址:

    https://gitee.com/pcore/just-auth-spring-security-starter

    开源软件介绍:

    Spring security 集成 JustAuth 实现第三方授权登录脚手架:

    justAuthSpringSecurityJDKMavenMySQLRedisSpringBootSpringSecurityJustAuthlicense

    一、特性

    Spring security 集成 JustAuth 实现第三方授权登录: 此项目从 用户管理脚手架(UMS):https://github.com/ZeroOrInfinity/UMS | https://gitee.com/pcore/UMS) 项目中分离.

    1. 支持所有 justAuth 支持的第三方登录,登录后自动注册 或 绑定 或 创建临时用户(TemporaryUser).
    2. 支持定时刷新 accessToken 分布式定时任务,
    3. 支持第三方授权登录的用户信息表与 token 信息表的 redis 缓存功能.
    4. 支持第三方绑定与解绑及查询接口.
    5. 支持一键登录.

    登录流程图

    登录流程图

    微信群:UMS 添加微信(z56133)备注(UMS)

    weixin


    二、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 容器即可替代.

      • UsersConnectionRepository: 第三方授权登录的第三方用户信息增删改查, 绑定与解绑及查询是否绑定与解绑接口, 一般不需要用户实现.除非想自定义获取第三方用户信息的逻辑, 实现此接口注入 IOC 容器即可替代.

      • UsersConnectionTokenRepository: 第三方授权登录用户 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);
    • 注意: UmsUserDetailsService的注册用户方法返回的 UserDetails 的默认实现 User 已实现反序列化器, 如果是开发者自定义的子类, 需开发者自己实现反序列化器.

    六、快速开始(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 工具依赖,项目内如果已有,请忽略。另外需要特别注意,如果项目中已经引入了低版本的依赖,请先排除低版本以后来,引入高版本或者最新版本的依赖

    • hutool-http
    <dependency>    <groupId>cn.hutool</groupId>    <artifactId>hutool-http</artifactId>    <version>5.2.5</version></dependency>
    • httpclient
    <dependency>	<groupId>org.apache.httpcomponents</groupId>  	<artifactId>httpclient</artifactId>  	<version>4.5.12</version></dependency>
    • okhttp
    <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.java

    import 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 到 HttpSecurity

    WebSecurityConfig.java

    import 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
                          

    鲜花

    握手

    雷人

    路过

    鸡蛋
    该文章已有0人参与评论

    请发表评论

    全部评论

    专题导读
    热门推荐
    阅读排行榜

    扫描微信二维码

    查看手机版网站

    随时了解更新最新资讯

    139-2527-9053

    在线客服(服务时间 9:00~18:00)

    在线QQ客服
    地址:深圳市南山区西丽大学城创智工业园
    电邮:jeky_zhao#qq.com
    移动电话:139-2527-9053

    Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap