前面已经介绍了JWT,接下来进入实战环节
1. 生成密钥证书 下边命令生成密钥证书,采用RSA 算法每个证书包含公钥和私钥创建一个文件夹,在该文件夹下执行如下命令行:
keytool -genkeypair -alias kaikeba -keyalg RSA -keypass kaikeba -keystore kaikeba.jks -storepass kaikeba
-alias:密钥的别名
-keyalg:使用的hash算法
-keypass:密钥的访问密码
-keystore:密钥库文件名
-storepass:密钥库的访问密码
当你直接复制然后cmd执行命令的时候,可能会这样
哈哈,问题不大,这时候我们执行目录改到jre下面。
这时候已经出来了。
keytool -list -keystore kaikeba.jks
这样就没问题了。
openssl是一个加解密工具包,这里使用openssl来导出公钥信息。
安装 openssl:http://slproweb.com/products/Win32OpenSSL.html
安装并且配置好环境变量之后,执行下面的命令
keytool -list -rfc --keystore kaikeba.jks | openssl x509 -inform pem -pubkey
下面段内容是公钥
-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhkw5XroxnOLSSakPjNvd 3QxV6Vr/cUXq+0xYDEKA3L7oxdHQP/BZMkHr63R14GsL24B3UtLMAlv/3xua5aE7 rJ54SX9h8i6yxhUb83qRU4rMU749goMazGOr3xaGXV7oXc3qYrKrGqP/Kzpeomsw FrYCJyVLkR4e5vwM/i6fbpQmVhxFOZSeekYOkCsL71oCtKcXMHMswGRt+xbCLgM/ 414SSJoX4EUSnUUaQPm7fi7nxLea/VSLlBuPhln2UR3xr4Zi1jlhIgKp/dMYMdQy Nm3THt+0CtDgelEkH+rVhpjFso1kTTV36+qP3UMSI0ekXAiEEVtpN3kLEmYqkvbO yQIDAQAB -----END PUBLIC KEY-----
将上边的公钥拷贝到文本public.key文件中,放到资源服务器中。
测试是否能够生成密钥
//生成一个jwt令牌 @Test public void testCreateJwt() throws Exception { //证书文件 String key_location = "kaikeba.jks"; //密钥库密码 String keystore_password = "kaikeba"; //访问证书路径 ClassPathResource resource = new ClassPathResource(key_location); //密钥工厂 KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource, keystore_password.toCharArray()); //密钥的密码,此密码和别名要匹配 String keypassword = "kaikeba"; //密钥别名 String alias = "kaikeba"; //密钥对(密钥和公钥) KeyPair keyPair = keyStoreKeyFactory.getKeyPair(alias, keypassword.toCharArray()); //私钥 RSAPrivateKey aPrivate = (RSAPrivateKey) keyPair.getPrivate(); //定义payload信息 MaptokenMap = new HashMap (); tokenMap.put("id", "123"); tokenMap.put("name", "malong"); tokenMap.put("roles", "r01,r02"); tokenMap.put("ext", "1"); //生成jwt令牌 Jwt jwt = JwtHelper.encode(new ObjectMapper().writevalueAsString(tokenMap), new RsaSigner(aPrivate)); //取出jwt令牌 String token = jwt.getEncoded(); System.out.println(token); }
校验合法性以及解码
//资源服务使用公钥验证jwt的合法性,并对jwt解码 @Test public void testVerify() { //jwt令牌 String token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHQiOiIxIiwicm9sZXMiOiJyMDEscjAyIiwibmFtZSI6Im1hbG9uZyIsImlkIjoiMTIzIn0.fjwhLSoiDk_zpyQMueBwYHbb5O5DMuvRghhxW9eZaH4qV6PHYtagEyrjSEjKiWnppqPrMn0hE1oTOVhMpRAYKGofUXJ2e04274b8YT6x6k9YC-s4bj9bckt74iGxqgslRHz0x9Zs7vK2EsywB23cNR-h6ZQyoEbPpsjwN6Knrwuv_YvxnUw6ZgGpg7QO0KRTUgNiytPaSABLZUETZQvU8ZTLCB_uwT5dkv4Si8J6107eZhzMYwvGawKtrmsG7M2iaMmegzSh6YRyX_FNjowW_L8fRobk3wbCR_VNgKVL5UU0B_XWurt2OoCH9xAjzSH6xZbJazQ1gpqXn05YAG05Wg"; //公钥 String publickey = "-----BEGIN PUBLIC KEY-----n" + "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhkw5XroxnOLSSakPjNvdn" + "3QxV6Vr/cUXq+0xYDEKA3L7oxdHQP/BZMkHr63R14GsL24B3UtLMAlv/3xua5aE7n" + "rJ54SX9h8i6yxhUb83qRU4rMU749goMazGOr3xaGXV7oXc3qYrKrGqP/Kzpeomswn" + "FrYCJyVLkR4e5vwM/i6fbpQmVhxFOZSeekYOkCsL71oCtKcXMHMswGRt+xbCLgM/n" + "414SSJoX4EUSnUUaQPm7fi7nxLea/VSLlBuPhln2UR3xr4Zi1jlhIgKp/dMYMdQyn" + "Nm3THt+0CtDgelEkH+rVhpjFso1kTTV36+qP3UMSI0ekXAiEEVtpN3kLEmYqkvbOn" + "yQIDAQABn" + "-----END PUBLIC KEY-----"; //校验jwt Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publickey)); //获取jwt原始内容 String claims = jwt.getClaims(); System.out.println(claims); try { Map搭建授权中心工程map = new ObjectMapper().readValue(claims, Map.class); System.out.println(map.get("user_name")); } catch (IOException e) { e.printStackTrace(); } }
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients @EnableCircuitBreaker public class AuthApplication { public static void main(String[] args) { SpringApplication.run(AuthApplication.class, args); } }
OAuth2配置类
import org.springframework.beans.factory.annotation.Qualifier; 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.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.client.JdbcClientDetailsService; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; 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; import javax.sql.DataSource; import java.util.concurrent.TimeUnit; @Configuration @EnableAuthorizationServer public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { final AuthenticationManager authenticationManager; private final DataSource dataSource; public AuthorizationServerConfiguration(@Qualifier("authenticationManagerBean") AuthenticationManager authenticationManager, DataSource dataSource) { this.authenticationManager = authenticationManager; this.dataSource = dataSource; } @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { //证书路径和密钥库密码 KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("kaikeba.jks"), "kaikeba".toCharArray()); JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); //密钥别名 converter.setKeyPair(keyStoreKeyFactory.getKeyPair("kaikeba")); return converter; } @Bean public ClientDetailsService clientDetailsService() { return new JdbcClientDetailsService(dataSource); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { //配置通过表oauth_client_details,读取客户端数据 clients.withClientDetails(clientDetailsService()); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { //tokenStore endpoints.tokenStore(tokenStore()).tokenEnhancer(jwtAccessTokenConverter()).authenticationManager(authenticationManager); //tokenService DefaultTokenServices tokenServices = new DefaultTokenServices(); tokenServices.setTokenStore(endpoints.getTokenStore()); tokenServices.setSupportRefreshToken(false); tokenServices.setClientDetailsService(endpoints.getClientDetailsService()); tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer()); tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); // 30天 endpoints.tokenServices(tokenServices); } @Override public void configure(AuthorizationServerSecurityConfigurer security) { // 允许表单认证 security.allowFormAuthenticationForClients() //放行oauth/token_key(获得公钥) .tokenKeyAccess("permitAll()") .checkTokenAccess("permitAll()"); } }
Spring Security配置类
@Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private final UserDetailsService userDetailsService; public SecurityConfiguration(UserDetailsService userDetailsService) { this.userDetailsService = userDetailsService; } @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http.requestMatchers().anyRequest() //开放/oauth/开头的所有请求 .and().authorizeRequests().antMatchers("/oauth/**").permitAll(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //注入自定义的UserDetailsService,采用BCrypt加密 auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } }
UserDetailsService
- JwtConfig
创建TokenStore配置使用公钥验证令牌
@Configuration public class JwtConfig { public static final String public_cert = "public.key"; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Bean @Qualifier("tokenStore") public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter); } @Bean protected JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); Resource resource = new ClassPathResource(public_cert); String publicKey; try { publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream())); }catch (IOException e) { throw new RuntimeException(e); } // 设置校验公钥 converter.setVerifierKey(publicKey); // 设置证书签名密码,否则报错 converter.setSigningKey("kaikeba"); return converter; } }
- ResourceServerConfiguration
配置资源服务器
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Autowired private TokenStore tokenStore; @Override public void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() // .antMatchers("/**").permitAll(); .antMatchers("/user/**").permitAll() .antMatchers("/book/**").hasRole("ADMIN") //用于测试 .antMatchers("/**").authenticated(); } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.tokenStore(tokenStore); } }权限控制
Spring Security中定义了四个支持权限控制的表达式注解,分别是
● @PreAuthorize
● @PostAuthorize
● @PreFilter和
● @PostFilter
其中前两者可以用来在方法调用前或者调用后进行权限检查,后两者可以用来对集合类型的参数或者返回值进行过滤。在需要控制权限的方法上,我们可以添加@PreAuthorize注解,用于方法执行前进行权限检查,校验用户当前角色是否能访问该方法。
- EnableGlobalMethodSecurity
使用上述注解控制权限需要设置
@EnableGlobalMethodSecurity(prePostEnabled = true)
** 控制权限方式**
(1)全局代码控制
(2)注解控制
(3)权限测试
使用Postman测试,分别获得管理员和刘国梁的令牌,其中管理员用户属于ADMIN角色,刘国梁不属于USER角色,这时使用刘国梁用户的令牌访问上述请求,会被拒绝
注意:此时关闭授权中心,可以看到资源中心依然可以使用JWT实现授权控制,说明使用资源中心在使用公钥验证令牌
如果希望一个方法能被多个角色访问,使用@PreAuthorize(“hasAnyAuthority(‘admin’,‘user’)”)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)