OAuth 2.0+JWT+spring security完成认

作者: rs汀 | 来源:发表于2020-03-14 13:47 被阅读0次

    1.基于OAuth 2.0+JWT+spring security完成认证授权

    首先分析一下为什么要用OAuth2和JWT来做

    1. 单点登录(SSO)方案单击登录方案是最常见的解决方案,但单点登录需要每个与用户交互的服务都必须与认证服务进行通信,这不但会造成重复,也会产生大量琐碎的网络流量
    2. 分布式会话(Session)方案通过将用户会话信息存储在共享存储中,如Redis,并使用用户会话的ID作为key来实现分布式哈希映射。当用户访问微服务时,会话数据就可以从共享存储中获取。该解决方案在高可用和扩展方面都很好,但是由于会话信息保存在共享存储中,所以需要一定的保护机制保护数据安全,因此在具体的实现中会具有比较高的复杂度
    3. 客户端令牌(Token)方案令牌由客户端生成,并由认证服务器签名。在令牌中会包含足够的信息,客户端在请求时会将令牌附加在请求上,从而为各个微服务提供用户身份数据。此方案解决了分布式会话方案的安全性问题,但如何及时注销用户认证信息则是一个大问题,虽然可以使用短期令牌并频繁地与认证服务器进行校验,但并不可以彻底解决。JWT(JSON Web Tokens)是非常出名的客户端令牌解决方案,它足够简单,并且对各种环境支持程度也比较高
    4. 客户端令牌与API网关结合
      通过在微服务架构中实施API网关,可以将原始的客户端令牌转换为内部会话令牌。一方面可以有效地隐藏微服务,另一方面通过API网关的统一入口可以实现令牌的注销处理。在David Borsos的第二个方案:分布式Session方案中要求开发者能够将用户会话信息单独拎出来进行集中管理。业界比较成熟的开源项目有Spring Session,其使用Redis数据库或缓存机制来实现Session存储,并通过过滤器实现Session数据的自动加载。随着近几年云服务应用的发展,基于令牌(Token)的认证使用范围也越来越广。对于基于令牌认证通常包含下面几层含义:
    • 令牌是认证用户信息的集合,而不仅仅是一个无意义的ID。
    • 在令牌中已经包含足够多的信息,验证令牌就可以完成用户身份的校验,从而减轻了因为用户验证需要检索数据库的压力,提升了系统性能。
    • 因为令牌是需要服务器进行签名发放的,所以如果令牌通过解码认证,我们就可以认为该令牌所包含的信息是合法有效的。
    • 服务器会通过HTTP头部中的Authorization获取令牌信息并进行检查,并不需要在服务器端存储任何信息。
    • 通过服务器对令牌的检查机制,可以将基于令牌的认证使用在基于浏览器的客户端和移动设备的App或是第三方应用上。
      ·可以支持跨程序调用。基于Cookie是不允许垮域访问的,而令牌则不存在这个问题。
    综上所述,基于令牌的认证由于会包含认证用户的相关信息,因此可以通过验证令牌来完成用户身份的校验,完全不同于之前基于会话的认证。因此,基于令牌的这个优点,像T微信、支付宝、微博及GitHub等,都推出了基于令牌的认证服务,用于访问所开放的API及单点登录。接下来将重点介绍基于令牌认证方案中的OAuth 2.0和JWT

    2.两部分:认证服务端(认证及生成token) 、认证资源服务端(访问其他服务内的资源需要校验)

    rs.png

    3.先看看 客户端授权模式(一般采用授权码模式,简单来说就是你要重定向url认证服务器获取授权码(code),在获取访问令牌。

    在上面流程图中第一步之后,会重定向到类似于

    http://localhost:8080/token/oauth/authorize?client_id=client1&response_type=code&redirect_uri=/token
    

    会返回一个code
    在访问

    http://localhost:8080/oauth/token?client_id=client1&grant_type=authorization_code&redirect_uri=/token&code=
    

    附加上code值,这时就会返回access_token

    还有一个问题:上面url的请求的路径需要保存在数据库中,需要新建一个表,固定的字段
    image.png
    CREATE TABLE `oauth_client_details` (
      `client_id` varchar(255) NOT NULL,
      `resource_ids` varchar(255) DEFAULT NULL,
      `client_secret` varchar(255) DEFAULT NULL,
      `scope` varchar(255) DEFAULT NULL,
      `authorized_grant_types` varchar(255) DEFAULT NULL,
      `web_server_redirect_uri` varchar(255) DEFAULT NULL,
      `authorities` varchar(255) DEFAULT NULL,
      `access_token_validity` int(11) DEFAULT NULL,
      `refresh_token_validity` int(11) DEFAULT NULL,
      `additional_information` varchar(4096) DEFAULT NULL,
      `autoapprove` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`client_id`) USING BTREE
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    详情见如后代码

    4.正式代码 (认证服务端)

    pom.xml文件

            <dependency>
                <groupId>org.springframework.security.oauth</groupId>
                <artifactId>spring-security-oauth2</artifactId>
                <version>2.3.4.RELEASE</version>
            </dependency>
            <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>
            </dependency>
    
    image.png

    主要讲解这三个类,他们的的执行顺序:SecurityConfiguration->MyAuthenticationSuccessHandler->AuthorizationServerConfiguration

    首先在用户中心登陆操作,获取账号密码,发送HTTP请求

    private JSONObject requestToken(String account, String password, String deviceType) {
            String result = null;
            try {
                RestTemplate restTemplate = new RestTemplate();
                HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
                HttpClient httpClient = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build();
                factory.setHttpClient(httpClient);
                restTemplate.setRequestFactory(factory);
    
                MultiValueMap<String, Object> sendMap = new LinkedMultiValueMap<>();
                sendMap.add("username", account);
                sendMap.add("password", password);
                result = RestTemplateUtil.postForEntityFormData(restTemplate, Datas.AUTH_LOGIN_URL, sendMap, deviceType);
                logger.info("认证中心返回结果-------》》》》》" + result);
            } catch (Exception e) {
                logger.error("error", e);
                throw new Exception("500", e);
            }
            return JSON.parseObject(result);
        }
    

    方法执行顺序:userDetailsService(会获取到传进来的username)->protected void configure(HttpSecurity http)
    ->这里会执行config里的myAuthenticationFailureHandler->(这是第二个类)

    import javax.transaction.Transactional;
    
    import org.bifu.distributed.auth.constant.AuthContants;
    import org.bifu.distributed.auth.dao.UserMapper;
    import org.bifu.distributed.auth.domain.User;
    import org.bifu.distributed.auth.dto.SecurityUserDTO;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    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.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    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;
    
    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
        
        private final static Logger logger = LoggerFactory.getLogger(SecurityConfiguration.class);
    
        @Autowired
        private UserMapper userMapper;
    
        @Autowired
        private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    
        @Autowired
        private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    
        @Autowired
        public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
            // 配置用户来源于数据库
    //      auth.userDetailsService(userDetailsService()).passwordEncoder(new MyPasswordEncoder());
            auth.userDetailsService(userDetailsService()).passwordEncoder(new BCryptPasswordEncoder());
    //      auth.userDetailsService(userDetailsService()).passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
    //      auth.userDetailsService(userDetailsService()).passwordEncoder(MyPasswordEncoderFactories.createDelegatingPasswordEncoder());
        }
    
        /**
         * authorizeRequests()配置路径拦截,表明路径访问所对应的权限,角色,认证信息。
         * formLogin()对应表单认证相关的配置
         * logout()对应了注销相关的配置
         * httpBasic()可以配置basic登录
         * @param http
         * @throws Exception
         */
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin().loginProcessingUrl("/auth/login").successHandler(myAuthenticationSuccessHandler)
                    .failureHandler(myAuthenticationFailureHandler).and().csrf().disable().sessionManagement()
                    .maximumSessions(1).expiredUrl("/expiredSession");
            logger.info("test是否生成token");
        }
    
        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
    
        @Override
        @Bean
        public UserDetailsService userDetailsService() {
            return new UserDetailsService() {
                @Override
                @Transactional(rollbackOn = Exception.class)
                public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                    //(注:用户名的name属性必须为username,密码的name属性必须为password。这是security判断用户输入是否正确的标准)
                    // 
                    logger.info("登录手机号或邮箱:======"+username);
                    // 查用户 
                    User user = userMapper.selectByPhoneOrEmail(username, username);
                    if (user == null) {
                        throw new UsernameNotFoundException(AuthContants.USER_NOT_EXIST);
                    }
                    SecurityUserDTO dto = new SecurityUserDTO();
                    dto.setId(user.getId());
                    dto.setUsername(username);
                    dto.setPassword(user.getPassword());
                    dto.setDisable(user.getDisable());
                    // 创建securityUserDTO
    //              SecurityUserDTO securityUserDTO = new SecurityUserDTO(user);
                    return dto;
                }
            };
        }
    
    }
    

    这是第二个类,这里会进行重定向获取code

    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.bifu.distributed.auth.constant.AuthContants;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
        private final static Logger logger = LoggerFactory.getLogger(MyAuthenticationSuccessHandler.class);
    
        @Value(value = "${prefix.auth}")
        private String authPrefix; // /token
    
        @Value(value = "${oauth.redirectUrl}")
        private String redirectUrl; // /token
    
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {
            String deviceType = request.getHeader("deviceType");
            logger.info("访问设备-----------》》》" + deviceType);
            if (deviceType == null || "".equals(deviceType)) {
                deviceType = "browser";
            }
            // 重定向url到 /token 接口
            if ("browser".equals(deviceType)) {
                response.sendRedirect("http://localhost:8080:oauth/authorize?client_id=client1&response_type=code&redirect_uri=/token");
            } else if ("app".equals(deviceType)) {
                response.sendRedirect(http://localhost:8080:oauth/authorize?client_id=client2&response_type=code&redirect_uri=/token);
            }
        }
    
    }
    

    因为重定向的url是:redirect_uri=/token

    5.在这一步之前需要进行服务端生成公钥和密钥,每个客户端使用获取到的公钥到服务器做认证。

    生成一个jks

      keytool -genkeypair -alias kevin_key -keyalg RSA -keypass 123456 -keystore kevin_key.jks -storepass 123456
    

    导出公钥

      keytool -list -rfc --keystore kevin_key.jks | openssl x509 -inform pem -pubkey
    

    保存文本public_key.txt

      -----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxeI6+R6DsGs5RW21Xu1Fur7iPwGjyngN3SCnwPtdR9jTrQ8EIak+gyjpI/g7gIacHIZKMlVFWoEgjQ7+hIQ5FHBrmSR/S81ezCFjYSjBbdrHYQjMRpn4mEWFmQhIyTRhg1Pb5oTUlWx+L3wc45r6JFdMOlgkKBvfo/7lzwGhxeNp10rfoJcnGDhlfZ3PmoIOYmvg7Z8UwszZpYHWf98164m3hMiPyc81iiy/DEE60OVVepyvynfBwg1aGDyA64w63FZ/2dSwfQ/7VQ7WWJb7oVoIy5pyHslWMuQJPpNCxpOgmb19AgC1GojDSL7WAEq+2gQFrb+7k4PyBdsRYzR9DQIDAQAB
    -----END PUBLIC KEY-----
    

    认证服务端在Resource保存jks,认证资源服务端保存public_key.txt


    image.png
    image.png

    下面类主要执行的方法是:accessTokenConverter

    import java.util.HashMap;
    import java.util.Map;
    
    import javax.sql.DataSource;
    
    import com.alibaba.fastjson.JSONObject;
    import org.bifu.distributed.auth.constant.AuthContants;
    import org.bifu.distributed.auth.dto.SecurityUserDTO;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
    import org.springframework.security.oauth2.common.OAuth2AccessToken;
    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.OAuth2Authentication;
    import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
    import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
    import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
    
    /**
     * 认证授权服务端
     *
     */
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
    
        private final static Logger logger = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);
    
        @Autowired
        private AuthenticationManager authenticationManager;
    
        @Autowired
        private DataSource dataSource;
    
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            //开启密码授权类型
            endpoints.authenticationManager(this.authenticationManager);
            endpoints.accessTokenConverter(accessTokenConverter());
            //配置token存储方式
            endpoints.tokenStore(tokenStore());
            endpoints.reuseRefreshTokens(false);
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')");
            oauthServer.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
        }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            // jdbc方式
            clients.withClientDetails(clientDetails());
        }
    
        /**
         * token converter
         * 
         * @return
         */
        @Bean
        public JwtAccessTokenConverter accessTokenConverter() {
            JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
                /***
                 * 重写增强token方法,用于自定义一些token返回的信息
                 */
                @Override
                public OAuth2AccessToken enhance(OAuth2AccessToken accessToken,
                        OAuth2Authentication authentication) {
                    SecurityUserDTO securityUserDTO =
                          (SecurityUserDTO) authentication.getUserAuthentication().getPrincipal();
                    logger.info("重写增强token方法= {}", JSONObject.toJSONString(securityUserDTO));
                    final Map<String, Object> additionalInformation = new HashMap<>(16);
                    additionalInformation.put("userId", securityUserDTO.getId());
                    ((DefaultOAuth2AccessToken) accessToken)
                            .setAdditionalInformation(additionalInformation);
                    OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
                    return enhancedToken;
                }
            };
            // 非对称加密
            KeyStoreKeyFactory keyStoreKeyFactory =
                    new KeyStoreKeyFactory(new ClassPathResource("kevin_key.jks"),
                            "123456".toCharArray());
            accessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("kevin_key"));
            return accessTokenConverter;
        }
    
        /**
         * 定义clientDetails存储的方式-》Jdbc的方式,注入DataSource
         *
         * @return
         */
        @Bean
        public ClientDetailsService clientDetails() {
            return new JdbcClientDetailsService(dataSource);
        }
    
        /**
         * token store
         * 
         * @param
         * @return
         */
        @Bean
        public TokenStore tokenStore() {
            TokenStore tokenStore = new JwtTokenStore(accessTokenConverter());
            return tokenStore;
        }
    
    }
    

    现在重定向到/token这个接口

    /**
         * 授权,登录
         */
        @ResponseBody
        @RequestMapping(value = "/token")
        public ResultDTO<TokenResultDTO> token(HttpServletRequest request, HttpServletResponse response,
                RedirectAttributes attributes) {
            TokenResultDTO result = this.userService.token(attributes, request, response);
            logger.info("获取到token= {}", JSONObject.toJSONString(result));
            return new ResultDTO<TokenResultDTO>("200", "succ", result);
        }
    
    
    //DTO类
    @Data
    public class TokenResultDTO {
    
        private String access_token;
    
        private String token_type;
    
        private String expires_in;
    
        private String scope;
    
        private String jti;
    
        private String refresh_token;
    
        private String userId;
    }
    
    访问的url路径 是固定的
     public TokenResultDTO token(RedirectAttributes attributes, HttpServletRequest request,
                HttpServletResponse response) {
            try {
    
                String code = request.getParameter("code");
                if (StringUtils.isEmpty(code)) {
                    throw new BusinessException(AuthContants.CODE_EXCEPTION);
                }
                // 发送请求token
                String deviceType = "browser";
                if (request.getHeader("deviceType") != null && !"".equals(request.getHeader("deviceType"))) {
                    deviceType = request.getHeader("deviceType");
                }
                HttpHeaders headers = new HttpHeaders();
                HttpEntity<String> entity = new HttpEntity<String>(headers);
                TokenResultDTO tokenResultDTO = null;
                if ("browser".equals(deviceType)) { 
                    tokenResultDTO = this.browserRestTemplate.postForObject(
                           " http://localhost/oauth/token?client_id=client1&grant_type=authorization_code&redirect_uri=/token&code=" + code,
                            entity, TokenResultDTO.class);
                } else if ("app".equals(deviceType)) {
                    tokenResultDTO = this.appRestTemplate.postForObject(
                             " http://localhost/oauth/token?client_id=client2&grant_type=authorization_code&redirect_uri=/token&code=" + code,
                            entity, TokenResultDTO.class);
                }
    
                return new TokenResultDTO(tokenResultDTO.getAccess_token(), tokenResultDTO.getRefresh_token(),
                        tokenResultDTO.getUserId(), tokenResultDTO.getExpires_in());
            } catch (BusinessException e) {
               
                logger.error("token?");
                throw new Exception("500", e.getMessage());
            } catch (Exception e) {
             
                logger.error("token?");
                throw new Exception("500", e.getMessage());
            }
        }
    

    到这里就获取到了access_token
    log日志:

    image.png

    二 认证资源服务端

    image.png

    这里只讲解ResourceServerConfiguration这个类,其他都是检查异常的

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;
    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;
    import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
    import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
    import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
    import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
    import org.springframework.security.web.AuthenticationEntryPoint;
    
    /**
     * 认证授权资源端
     * 
     * @author rs
     *
     */
    @Configuration
    @EnableResourceServer
    public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    
        @Autowired
        private MyAccessDeniedHandler myAccessDeniedHandler;
    
        @Autowired
        private Auth2ResponseExceptionTranslator auth2ResponseExceptionTranslator;
    
        @Autowired
        private SecurityAuthenticationEntryPoint securityAuthenticationEntryPoint;
    
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId("client1");
            resources.tokenServices(defaultTokenServices());
            // 定义异常转换类生效
            AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
            ((OAuth2AuthenticationEntryPoint) authenticationEntryPoint)
                    .setExceptionTranslator(this.auth2ResponseExceptionTranslator);
            resources.authenticationEntryPoint(authenticationEntryPoint);
        }
    
        @Override
        public void configure(HttpSecurity http) throws Exception {
            // 放行路径在这写
            http.csrf().disable().exceptionHandling().authenticationEntryPoint(this.securityAuthenticationEntryPoint)
                    .accessDeniedHandler(myAccessDeniedHandler).and().authorizeRequests()
                    .antMatchers("/swagger-resources/**", "/v2/**", "/swagger/**", "/swagger**", "/webjars/**", "/aide/**",
                            "/backstage/**", "/coin/**", "/talla/**", "/asset/**", "/test/**","/blockchain/borrow/**", "/back/**")
                    .permitAll().anyRequest().authenticated().and().httpBasic().disable();
            // ifream的跨域设置
            http.headers().frameOptions().sameOrigin();
        }
    
        // ===================================================以下代码与认证服务器一致=========================================
        /**
         * token存储,这里使用jwt方式存储
         * 
         * @param
         * @return
         */
        @Bean
        public TokenStore tokenStore() {
            TokenStore tokenStore = new JwtTokenStore(accessTokenConverter());
            return tokenStore;
        }
    
        /**
         * 创建一个默认的资源服务token
         * 
         * @return
         */
        @Bean
        public ResourceServerTokenServices defaultTokenServices() {
            final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
            defaultTokenServices.setTokenEnhancer(accessTokenConverter());
            defaultTokenServices.setTokenStore(tokenStore());
            return defaultTokenServices;
        }
    
        /**
         * Token转换器必须与认证服务一致
         * 
         * @return
         */
        @Bean
        public JwtAccessTokenConverter accessTokenConverter() {
            JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
            };
            Resource resource = new ClassPathResource("public_key.txt");
            String publicKey = null;
            try {
                publicKey = inputStream2String(resource.getInputStream());
            } catch (final IOException e) {
                throw new RuntimeException(e);
            }
            accessTokenConverter.setVerifierKey(publicKey);
            return accessTokenConverter;
        }
        // ===================================================以上代码与认证服务器一致=========================================
    
        private String inputStream2String(InputStream is) throws IOException {
            BufferedReader in = new BufferedReader(new InputStreamReader(is));
            StringBuffer buffer = new StringBuffer();
            String line = "";
            while ((line = in.readLine()) != null) {
                buffer.append(line);
            }
            return buffer.toString();
        }
    
    }
    

    主要讲解public void configure(HttpSecurity http)这个方法:我们在测试的时候往往希望直接就能访问到,不需要token,可以在这里添加放行路径。

    个人理解,欢迎指正交流哦!!!,随后会把代码公布在gitee中:https://gitee.com/ran_song

    IMG_2423.JPG

    相关文章

      网友评论

        本文标题:OAuth 2.0+JWT+spring security完成认

        本文链接:https://www.haomeiwen.com/subject/atemshtx.html