SpringSecurity+Oauth2+JWT安全框架(一)


一、添加依赖

在这里我们也需要导入redis的相关依赖,因为实际使用过程,需要人为干预令牌的有效时间

<!--Oauth2依赖-->
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>${security-oauth2-version}</version>
</dependency>
<!--使用jwt令牌-->
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-jwt</artifactId>
    <version>${jwt-version}</version>
</dependency>
<!-- 配置使用redis启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>${redis-jedis-version}</version>
</dependency>

<!--fastjson转换-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>${alibaba-fastjon-version}</version>
</dependency>

 

二、生成jks证书

在导入依赖包后, 我们需要再redsources文件下创建配置文件application.yml,在这里我们不做过多的说明,我们要使用到JWT安全证书,并通过证书生成令牌,并将生成的证书文件xxx.jks放到resources文件下。

 

三、创建SecurityConfiguration.java实体类,并让其继承WebSecurityConfigurerAdapt,其中myUserDetailsServiceImpl是进行验证登陆者账号和密码的实体类

@Configuration
@EnableWebSecurity
class CustomWebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource(name = "myUserDetailsServiceImpl")
    private UserDetailsService userDetailsService;


    @Override
    public void configure(WebSecurity web) throws Exception {
        //用户登录认证、用户退出、用户获取jwt令牌
        web.ignoring().antMatchers("/XXX");
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        AuthenticationManager manager = super.authenticationManagerBean();
        return manager;
    }

    @Override
    protected UserDetailsService userDetailsService() {
        return userDetailsService;
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    //采用bcrypt对密码进行编码
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
//        http.csrf().disable()
//                .httpBasic().and()
//                .formLogin()
//                .and()
//                .authorizeRequests().anyRequest().authenticated();
        http.authorizeRequests().anyRequest().permitAll().and().csrf().disable();

    }

}

 

四、UserDetailsServiceImpl实体类,我们可以看到这里做了账号密码验证并且使用了加密

 

五、创建实体类AuthServerConfig.java实体类,该实体类做了关于oauth2客户端的一些配置

@Configuration
@EnableAuthorizationServer
public class CustomAuthServerConfig extends AuthorizationServerConfigurerAdapter {

    /*jwt令牌转换器*/
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    /*在这里userDetailService一定要加上@Autowired,否则会报错找不到服务器UserDetailsService is required*/
    @Resource(name = "myUserDetailsServiceImpl")
    UserDetailsService userDetailsService;
    @Autowired
    AuthenticationManager authenticationManager;
    @Autowired
    TokenStore tokenStore;
    @Autowired
    PasswordEncoder passwordEncoder;
    @Autowired
    private SecurityAuth securityAuth;

    /* 授权服务器的安全配置*/
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer
                .tokenKeyAccess("permitAll()")
                .allowFormAuthenticationForClients()
                /*校验token需要认证通过,可采用http basic认证*/
                .checkTokenAccess("isAuthenticated()");
    }

    /*设置认证请求相关参数,例如客户端账号和密码、令牌有效时间等等*/
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient(securityAuth.getClientId())/*客户端id*/
                .secret(passwordEncoder.encode(securityAuth.getClientSecret()))/*密码,要保密*/
                .accessTokenValiditySeconds(60 * 60 * X)/*访问令牌有效期X个小时*/
.refreshTokenValiditySeconds(60 * 60 * X)/*刷新令牌有效期X个小时*/
/*授权客户端请求认证服务的类型authorization_code:根据授权码生成令牌, client_credentials:客户端认证,refresh_token:刷新令牌,password:密码方式认证*/ .authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token", "password") .redirectUris("http://localhost") .scopes("app");/*客户端范围,名称自定义,必填*/ } @Bean public TokenEnhancer customTokenEnhancer() { return new CustomTokenEnhancer(); } /*授权服务器端点配置*/ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { /*endpoints.authenticationManager(authenticationManager)认证管理器 .userDetailsService(userDetailsService);/*用户信息service*/ /*将增强的token设置到增强链中*/ TokenEnhancerChain enhancerChain = new TokenEnhancerChain(); enhancerChain.setTokenEnhancers(Arrays.asList(customTokenEnhancer(), jwtAccessTokenConverter())); endpoints.accessTokenConverter(jwtAccessTokenConverter) .authenticationManager(authenticationManager)/*认证管理器*/ .tokenStore(tokenStore)/*令牌存储*/ .userDetailsService(userDetailsService)/*用户信息service*/ .tokenEnhancer(enhancerChain);//自定义jwt令牌中的携带参数 } //使用证书文件生成jwt令牌的 @Bean @Autowired public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) { return new JwtTokenStore(jwtAccessTokenConverter); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { /* 证书文件*/ String key_location = "XXXX.jks";
/*密钥库密码*/ String keystore_password = "XXXX";
/*访问证书路径*/ ClassPathResource resource = new ClassPathResource(key_location); /*密钥工厂*/ KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource, keystore_password.toCharArray()); /*密钥别名*/ String alias = "XXXX";
/*密钥库密码*/ String keypassword = "XXXX"; KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, keypassword.toCharArray()); JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setKeyPair(keyPair); return converter; } }

 这样认证服务器的配置参数就完成了,启动服务后,我们就可以测试发送验证请求,因为在这里我们仅仅叙述的是功能的实现,账号密码都用一些最简单的,这块的业务逻辑,可以根据实际情况去编写,是否能获取到返回的令牌

 

六、测试

密码模式测试

Post请求:http://localhost:8080/oauth/token

参数:

grant_type:密码模式授权填写password

username:账号

password:密码

此链接需要使用http Basic认证,这点需要注意以下,http Basic中认证信息的username和password就写我们之前在代码中预先写好的内容

access_token:访问令牌,携带此令牌访问资源

token_type:在请求资源的时候,在令牌之前加上该信息,并且空一格

refresh_token:刷新令牌,使用此令牌可以延长访问令牌的过期时间

expires_in:过期时间,单位为秒

scope:范围,与定义的客户端范围一致

 

看到以上信息的时候,恭喜你测试成功!


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM