• springboot oauth 鉴权之——授权码authorization_code鉴权


     近期一直在研究鉴权方面的各种案例,这几天有空,写一波总结及经验。

    第一步:什么是 OAuth鉴权

           OAuth2是工业标准的授权协议。OAuth2取代了在2006创建的原始OAuthTM协议所做的工作。OAuth 2.0关注客户端开发人员的简单性,同时为Web应用程序、桌面应用程序、移动电话和客厅设备提供特定的授权流。

         参考理解:  Oauth2.0     理解OAuth 2.0     QQ授权      微信授权

    第二步:什么是授权码模式

            授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。

    第三步:授权码授权流程

            

    交互步骤: 
    (A)用户访问客户端,后者将前者导向认证服务器。 (B)用户选择是否给予客户端授权。 (C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。 (D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。 (E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

    第四步:授权码模式实践

           源码地址:

                   github: https://github.com/GitHubZhangCom/spring-security-oauth-example/

                   码云:https://gitee.com/region/spring-security-oauth-example/

     1)、  引入相关jar: 

    <!--security  -->
    <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
    <dependency>
           <groupId>org.springframework.security.oauth.boot</groupId>
           <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.0.0.RELEASE</version>
    </dependency>
    <!-- 使用lombok优雅的编码 -->
    <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
    </dependency>

     2)、相关oauth编程: 

    package com.auth.server.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    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.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.UserDetailsService;
    
    /**
     * 鉴权配置
     * @author zyl
     *
     */
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Bean
        @Override
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
        @Qualifier("myUserDetailsService")
        @Autowired
        private UserDetailsService userDetailsService;
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService);
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.authorizeRequests()
                    .antMatchers("/**/*.js",
                            "/**/*.css"
                    )
                    .permitAll()
                    .anyRequest()
                    .authenticated()
                    .and()
                    .formLogin()
                    .loginPage("/login")
                    .permitAll()
                    // 自动登录
                    /*.and()
                        .rememberMe()
                        // 加密的秘钥
                        .key("unique-and-secret")
                        // 存放在浏览器端cookie的key
                        .rememberMeCookieName("remember-me-cookie-name")
                        // token失效的时间,单位为秒
                        .tokenValiditySeconds(60 * 60 * 25)*/
                    .and()
                    // 暂时禁用CSRF,否则无法提交登录表单
                    .csrf().disable();
    
        }
    
    }
    package com.auth.server.config;
    
    import javax.sql.DataSource;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    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.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
    
    /**
     * 授权配置
     * @author wb0024
     *
     */
    @Configuration
    @EnableAuthorizationServer
    public class ServerConfig extends AuthorizationServerConfigurerAdapter {
        @Autowired
        private AuthenticationManager authenticationManager;
    
    
        @Qualifier("myUserDetailsService")
        @Autowired
        private UserDetailsService userDetailsService;
    
    //    @Autowired
    //    @Qualifier("dataSource")
    //    private DataSource dataSource;
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            // 配置token获取和验证时的策略
            security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
        }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient("client")
                    // secret密码配置从 Spring Security 5.0开始必须以 {加密方式}+加密后的密码 这种格式填写
                    
                    /* *   当前版本5新增支持加密方式:
                     *   bcrypt - BCryptPasswordEncoder (Also used for encoding)
                     *   ldap - LdapShaPasswordEncoder
                     *   MD4 - Md4PasswordEncoder
                     *   MD5 - new MessageDigestPasswordEncoder("MD5")
                     *   noop - NoOpPasswordEncoder
                     *   pbkdf2 - Pbkdf2PasswordEncoder
                     *   scrypt - SCryptPasswordEncoder
                     *   SHA-1 - new MessageDigestPasswordEncoder("SHA-1")
                     *   SHA-256 - new MessageDigestPasswordEncoder("SHA-256")
                     *   sha256 - StandardPasswordEncoder*/
                     
                    .secret("{noop}secret")
                    .scopes("all")
                    .authorizedGrantTypes("authorization_code")//授权码模式
    //                .authorizedGrantTypes("authorization_code", "refresh_token")//授权码模式
                    .autoApprove(true);
        }
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    //        // 配置tokenStore,保存到redis缓存中
    //        endpoints.authenticationManager(authenticationManager)
    //                .tokenStore(new MyRedisTokenStore(redisConnectionFactory))
    //                // 不添加userDetailsService,刷新access_token时会报错
    //                .userDetailsService(userDetailsService);
    
            // 使用最基本的InMemoryTokenStore生成token
            endpoints.authenticationManager(authenticationManager).tokenStore(memoryTokenStore());
    
        }
    
        // 使用最基本的InMemoryTokenStore生成token
        @Bean
        public TokenStore memoryTokenStore() {
            return new InMemoryTokenStore();
        }
    }

     

    package com.jwt.server.provider;
    
    import java.util.ArrayList;
    
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    
    
    /**
     * 自定义身份认证验证组件
     * @author zyl
     *
     */
    public class CustomAuthenticationProvider implements AuthenticationProvider {
    
        private UserDetailsService userDetailsService;
    
        private BCryptPasswordEncoder bCryptPasswordEncoder;
    
        public CustomAuthenticationProvider(UserDetailsService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder){
            this.userDetailsService = userDetailsService;
            this.bCryptPasswordEncoder = bCryptPasswordEncoder;
        }
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            // 获取认证的用户名 & 密码
            String name = authentication.getName();
            String password = authentication.getCredentials().toString();
            // 认证逻辑
            UserDetails userDetails = userDetailsService.loadUserByUsername(name);
            if (null != userDetails) {
                if (bCryptPasswordEncoder.matches(password, userDetails.getPassword())) {
                    // 这里设置权限和角色
                    ArrayList<GrantedAuthority> authorities = new ArrayList<>();
                    authorities.add( new GrantedAuthorityImpl("ROLE_ADMIN"));
                    authorities.add( new GrantedAuthorityImpl("ROLE_API"));
                    authorities.add( new GrantedAuthorityImpl("AUTH_WRITE"));
                    // 生成令牌 这里令牌里面存入了:name,password,authorities, 当然你也可以放其他内容
                    Authentication auth = new UsernamePasswordAuthenticationToken(name, password, authorities);
                    return auth;
                } else {
                    throw new BadCredentialsException("密码错误");
                }
            } else {
                throw new UsernameNotFoundException("用户不存在");
            }
        }
    
        /**
         * 是否可以提供输入类型的认证服务
         * @param authentication
         * @return
         */
        @Override
        public boolean supports(Class<?> authentication) {
            return authentication.equals(UsernamePasswordAuthenticationToken.class);
        }
    
    }

     核心代码需要做的就这些,详情请看源码。

  • 相关阅读:
    前沿科技相关
    52ABP
    C#常用及注意点
    电商秒杀系统:电商微服务框架组件
    面向对象OOP
    《CLR via C#》书籍
    .NET发布时选择【独立部署模式】引发的故事
    unity 3D物体使用EventSystem响应事件
    协程
    unity 2d碰撞/ui组件碰撞
  • 原文地址:https://www.cnblogs.com/haoliyou/p/9606018.html
Copyright © 2020-2023  润新知