Spring Security(十五):Token配置

一:配置多个Client并持久化到Redis中

@Data
@ToString
@AllArgsConstructor
@RequiredArgsConstructor
public class OAuth2Client {
    private String clientId;
    private String clientSecret;
    private int accessTokenValiditySeconds;

}
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        List<OAuth2Client> clientList = Arrays.asList(
                new OAuth2Client("clientId", "clientSecret", 7200),
                new OAuth2Client("clientId2", "clientSecret2", 0)
        );

        InMemoryClientDetailsServiceBuilder inMemory = clients.inMemory();
        // 可以通过多次调用withClient来配置多对clientId和secret
        clientList.forEach(client -> {
            inMemory // 使用in-memory存储
                    .withClient(client.getClientId())
                    .secret(new BCryptPasswordEncoder().encode(client.getClientSecret()))
                    // token的生效时间2小时, 0 表示永久生效不过期
                    .accessTokenValiditySeconds(client.getAccessTokenValiditySeconds())
                    .authorizedGrantTypes("authorization_code", "refresh_token", "password", "implicit")
                    .scopes("all", "read", "write")
                    .redirectUris("http://www.baidu.com");
        });
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(redisTokenStore())
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService);
    }

    @Bean
    public TokenStore redisTokenStore() {
        return new RedisTokenStore(redisConnectionFactory());
    }

    public RedisConnectionFactory redisConnectionFactory() {
        return new JedisConnectionFactory();
    }
}
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
	
    /**
     * 解决报错:'AuthenticationManager' that could not be found
     * org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.authentication.AuthenticationManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
     * @return
     * @throws Exception
     */
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
}

在这里插入图片描述

在这里插入图片描述

二:配置JWT

oauth2中生成的token是一个uuid值,其值本身是没有任何意义的,我们可以将替换成json web token, 简称 JWT

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

认证服务器

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    /**
     * 这里会注入失败
     */
    @Autowired
    private UserDetailsService userDetailsService;


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        List<OAuth2Client> clientList = Arrays.asList(
                new OAuth2Client("clientId", "clientSecret", 7200),
                new OAuth2Client("clientId2", "clientSecret2", 0)
        );

        InMemoryClientDetailsServiceBuilder inMemory = clients.inMemory();
        // 可以通过多次调用withClient来配置多对clientId和secret
        clientList.forEach(client -> {
            inMemory // 使用in-memory存储
                    .withClient(client.getClientId())
                    .secret(new BCryptPasswordEncoder().encode(client.getClientSecret()))
                    // token的生效时间2小时, 0 表示永久生效不过期
                    .accessTokenValiditySeconds(client.getAccessTokenValiditySeconds())
                    .refreshTokenValiditySeconds(259200)
                    .authorizedGrantTypes("authorization_code", "refresh_token", "password", "implicit")
                    .scopes("all", "read", "write")
                    .redirectUris("http://www.baidu.com");
        });
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .tokenStore(tokenStore())
                .userDetailsService(new MyUserDetailsService(new BCryptPasswordEncoder()));


        // jwt增强器
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> enhancers = new ArrayList<>();
        enhancers.add(jwtTokenEnhancer());
        enhancers.add(jwtAccessTokenConverter());
        enhancerChain.setTokenEnhancers(enhancers);

        endpoints
                .tokenEnhancer(enhancerChain)
                .accessTokenConverter(jwtAccessTokenConverter());
    }


    /**
     * 将token存储到redis中
     * @return
     */
//    @Bean
//    public TokenStore tokenStore() {
//        return new RedisTokenStore(redisConnectionFactory());
//    }
//
//    public RedisConnectionFactory redisConnectionFactory() {
//        return new JedisConnectionFactory();
//    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey("myJwtKey");
        return accessTokenConverter;
    }

    /**
     * token增强器
     * @return
     */
    @Bean
    public TokenEnhancer jwtTokenEnhancer() {
        return new JwtTokenEnhancer();
    }
}

Token增强器

public class JwtTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        // 在jwt中添加自定义字段
        Map<String, Object> info = new HashMap<>();
        info.put("foo", "bar");

        ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(info);
        return accessToken;
    }
}
@Slf4j
@RestController
public class UserController {

    @Autowired
    private ObjectMapper objectMapper;

    @GetMapping(value = "/user/me", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public String me(Authentication auth, HttpServletRequest request) throws JsonProcessingException, UnsupportedEncodingException {
        String header = request.getHeader("Authorization");
        String token = header.split("bearer ")[1];
        Claims claims = Jwts.parser().setSigningKey("myJwtKey".getBytes("UTF-8")).parseClaimsJws(token).getBody();
        String foo = (String) claims.get("foo");

        System.out.println(foo);

        return objectMapper.writeValueAsString(auth);
    }
}

获取token
在这里插入图片描述
jwt access_token字符串可以通过https://www.jsonwebtoken.io/进行解析
在这里插入图片描述

通过access_token访问用户信息
在这里插入图片描述
访问oauth/token并传参refresh_token获取新的access_token
在这里插入图片描述

发布了308 篇原创文章 · 获赞 936 · 访问量 133万+

猜你喜欢

转载自blog.csdn.net/vbirdbest/article/details/94587311