赞
踩
json web token
紧凑 且自包含的标准
HMAC算法 或 RSA(非对称加密)对 JWT进行签名
JWT 构成,3部分, 分别 . 分隔
xx.yy.zzz
两部分构成:令牌类型 和 使用的算法类型
令牌类型:HMAC,SHA256,RSA
{
"alg":"HS256",
"typ":"JWT"
}
使用 Base64
用户信息 和 Claim (声明,权利)
有三种类型的 Claim : 保留,公开,和 私人
{
"sub":"12321321",
"name":"John Doe",
"admin":true
}
同样用 Base64 编码
将 base64 编码后的 header,payload 和 秘钥 进行签名
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
浏览器登录 ——
服务器用 秘钥创建 JWT,返回给浏览器
浏览器在 header加 JWT,请求接口
服务器获取JWT 解密,处理逻辑,响应
eureka: instance: hostname: localhost client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 这种简单 eureka: client: register-with-eureka: false fetch-registry: false serviceUrl: defaultZone: http://localhost:${server.port}/eureka/
User Account and Authentication 用户帐号及认证
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
web eureka-client jpa mysql-java
spring: application: name: uaa-service datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/spring-cloud-auth?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8 username: root password: 123456 #都是 毫无亮点, driver-class-name url username password jpa: hibernate: ddl-auto: update show-sql: true server: port: 9999
@Configuration class WebSecurityConfig extends Web Security Configurer Adapter { @Override @Bean //authenticationManagerBean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .exceptionHandling() .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) //401 .and() .authorizeRequests() //所有请求,都要认证 .antMatchers("/**").authenticated() .and() .httpBasic(); } @Autowired UserServiceDetail userServiceDetail; //user Service Detail @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userServiceDetail) .passwordEncoder(new BCryptPasswordEncoder()); } }
@Service public class UserServiceDetail implements UserDetailsService { @Autowired private UserDao userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.findByUsername(username); } } public interface UserDao extends JpaRepository<User, Long> { User findByUsername(String username); }
@Configuration @EnableAuthorizationServer //开启 授权server public class OAuth2Config extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("user-service") //client secret scopes 权限类型 .secret("123456") .scopes("service") .autoApprove(true) .authorizedGrantTypes("implicit", "refresh_token", "password", "authorization_code") //都支持了 .accessTokenValiditySeconds(12 * 300);//5min过期 } @Autowired @Qualifier("authenticationManagerBean") private AuthenticationManager authenticationManager; @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtTokenEnhancer()); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { //JwtToken存储,解析器 也是 Jwt endpoints.tokenStore(tokenStore()).tokenEnhancer(jwtTokenEnhancer()).authenticationManager(authenticationManager); } @Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { oauthServer .tokenKeyAccess("permitAll()") .checkTokenAccess("isAuthenticated()").allowFormAuthenticationForClients().passwordEncoder(NoOpPasswordEncoder.getInstance()); /** * 必须设置allowFormAuthenticationForClients 否则没有办法用postman获取token * 也需要指定密码加密方式BCryptPasswordEncoder */ } //读取jks 文件 @Bean protected JwtAccessTokenConverter jwtTokenEnhancer() { KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("fzp-jwt.jks"), "fzp123".toCharArray()); JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); //这里测试后,作者的 文件可能已经失效,重新生成的 可用。 clean 在package converter.setKeyPair(keyStoreKeyFactory.getKeyPair("fzp-jwt")); return converter; } }
OAuth2的四种方式
模式
授权码(authorization-code)
隐藏式(implicit)
密码式(password)
客户端凭证(client credentials)
keytool -genkeypair -alias fzp-jwt -validity 3650 -keyalg RSA -dname "CN=jwt,OU=jtw,O=jtw,L=zurich,S=zurich,C=CH" -keypass fzp123 -keystore fzp-jwt.jks -storepass fzp123
alias 为别名
keypass storepass 为密码选项
validity 配置jks 文件的过期时间,天
keytool -list -rfc --keystore fzp-jwt.jks | openssl x509 -inform pem -pubkey
openssl 需要安装,不巡行这句,可以。输入密码 fzp123
命名为:public.cert,复制到 user-service(非uaa-servicce)的项目下。linux运行可行
certificate
英 /səˈtɪfɪkət/ 美 /sərˈtɪfɪkət/ 全球(英国)
简明 牛津 新牛津 韦氏 柯林斯 例句 百科
n. 证书;文凭,合格证书;电影放映许可证
v. 发给证明书,用证书证明
复数 certificates第三人称单数 certificates现在分词 certificating过去式 certificated过去分词 certificated
<build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <nonFilteredFileExtensions> <nonFilteredFileExtension>cert</nonFilteredFileExtension> <nonFilteredFileExtension>jks</nonFilteredFileExtension> </nonFilteredFileExtensions> </configuration> </plugin> </plugins> </build>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
openfeign hystrix eureka-client mysql-connector jpa web
server.port: 9090 eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ spring: application: name: user-service datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/spring-cloud-auth?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8 username: root password: 123456 jpa: hibernate: ddl-auto: update show-sql: true feign: hystrix: enabled: true
server port: 9090
eureka
application name
mysql
jpa
feign:
hystrix:
enabled: true
@Configuration public class JwtConfig { //jwt转换器 @Autowired JwtAccessTokenConverter jwtAccessTokenConverter; //token 存储 @Bean @Qualifier("tokenStore") public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter); } //jwt token en hancer @Bean protected JwtAccessTokenConverter jwtTokenEnhancer() { // jwt JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); // 创建 resource Resource resource = new ClassPathResource("public.cert"); String publicKey ; try { // 获得公钥, 通过 File CopyUtils.copyToByteArray,返回byte[] publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream())); } catch (IOException e) { throw new RuntimeException(e); } //公钥 复制给 jwt转换器,然后返回 converter.setVerifierKey(publicKey); return converter; } }
@Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter{ Logger log = LoggerFactory.getLogger(ResourceServerConfig.class); @Override public void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() //登录和注册 无需权限 .antMatchers("/user/login","/user/register").permitAll() .antMatchers("/**").authenticated(); } @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId("foo").tokenStore(tokenStore); } @Autowired TokenStore tokenStore; @Autowired JwtAccessTokenConverter tokenConverter; }
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class GlobalMethodSecurityConfig {
}
public interface UserDao extends JpaRepository<User, Long> {
User findByUsername(String username);
}
@Service public class UserServiceDetail implements UserDetailsService { @Autowired private UserDao userRepository; @Autowired //权限 服务 client ,就是下面自对应的 feing AuthServiceClient client; @Override //查看用户名的不变 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.findByUsername(username); } //插入用户,用户名 密码 public User insertUser(String username,String password){ User user=new User(); user.setUsername(username); //密码 存储,使用 加密的 user.setPassword(BPwdEncoderUtil.BCryptPassword(password)); return userRepository.save(user); } //登录用户 public UserLoginDTO login(String username,String password){ //查询出用户 User user=userRepository.findByUsername(username); if (null == user) { throw new UserLoginException("error username"); } //如果密码 不匹配,就扔异常。 参数的 password是明文 if(!BPwdEncoderUtil.matches(password,user.getPassword())){ throw new UserLoginException("error password"); } // 获取token,从 那个 hi-service的 basic 64中获取 JWT jwt=client.getToken("Basic dXNlci1zZXJ2aWNlOjEyMzQ1Ng==","password",username,password); // 获得用户菜单 if(jwt==null){ throw new UserLoginException("error internal"); } //设置上Jwt UserLoginDTO userLoginDTO=new UserLoginDTO(); userLoginDTO.setJwt(jwt); userLoginDTO.setUser(user); return userLoginDTO; } }
public class BPwdEncoderUtil { private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); /** * 用BCryptPasswordEncoder * @param password * @return */ public static String BCryptPassword(String password){ return encoder.encode(password); } /** * * @param rawPassword * @param encodedPassword * @return */ public static boolean matches(CharSequence rawPassword, String encodedPassword){ //校验是否匹配 return encoder.matches(rawPassword,encodedPassword); } }
@RestController @RequestMapping("/user") public class UserController { @Autowired UserServiceDetail userServiceDetail; @PostMapping("/register") public User postUser(@RequestParam("username") String username ,@RequestParam("password") String password){ //参数判断,省略 return userServiceDetail.insertUser(username,password); } @PostMapping("/login") public UserLoginDTO login(@RequestParam("username") String username , @RequestParam("password") String password){ //参数判断,省略 return userServiceDetail.login(username,password); } }
@RestController @RequestMapping("/foo") public class WebController { @RequestMapping(method = RequestMethod.GET) @PreAuthorize("hasAuthority('ROLE_ADMIN')") public String getFoo() { return "i'm foo, " + UUID.randomUUID().toString(); } } @Component public class AuthServiceHystrix implements AuthServiceClient { @Override public JWT getToken(String authorization, String type, String username, String password) { return null; } }
feign开启
@FeignClient(value = "uaa-service",fallback =AuthServiceHystrix.class )
public interface AuthServiceClient {
//一个 Header请求。 3个参数请求
@PostMapping(value = "/oauth/token")
JWT getToken(@RequestHeader(value = "Authorization") String authorization,
@RequestParam("grant_type") String type,
@RequestParam("username") String username,
@RequestParam("password") String password);
}
public class UserLoginDTO {
private JWT jwt;
private User user;
}
public class JWT {
private String access_token;
private String token_type;
private String refresh_token;
private int expires_in;
private String scope;
private String jti;
}
@ControllerAdvice //切面
@ResponseBody //json 返回
public class ExceptionHandle {
//拦截 这个自定义异常
@ExceptionHandler(UserLoginException.class)
public ResponseEntity<String> handleException(Exception e) {
//返回 实体
return new ResponseEntity(e.getMessage(), HttpStatus.OK);
}
}
//继承:RuntimeException
public class UserLoginException extends RuntimeException{
public UserLoginException(String message) {
super(message);
}
}
http://localhost:9090/user/register?username=miyaaa&password=123456
{
"id": 19,
"username": "miyaaa",
"password": "$2a$10$jMPZ6vBpKlIOpnSzUAeHXOmVqSERY6lkyLTZ95SuQc6t9NYlwrlzi",
"authorities": null,
"enabled": true,
"credentialsNonExpired": true,
"accountNonExpired": true,
"accountNonLocked": true
}
http://localhost:9090/user/login?username=miya6&password=123456
{ "jwt": { "access_token": "eyJhbGciOiJSUzIJ", "token_type": "bearer", "refresh_token": "eyJdafsadfds", "expires_in": 3599, "scope": "service", "jti": "36da2e6b-98aa-42f5-a51a-b951ce7d0179" }, "user": { "id": 21, "username": "miya6", "password": "$2a$10$3Vw8tvtgeu7i9JYuNBiaP.U6UBV5AKkYotTWKHW.Q09IvzFbHnMvC", "authorities": [], "enabled": true, "accountNonLocked": true, "credentialsNonExpired": true, "accountNonExpired": true } }
http://localhost:9999/auth/token?grant_type=password&username=miya6&password=123456
很遗憾,这个接口 后台打一个日志,返回为空。但是feign就feign通了
header是
Authorization
Basic dXNlci1zZXJ2aWNlOjEyMzQ1Ng==
http://localhost:9090/foo
Authorization
Bearer eyJhbGciOiJSUz ,如果没加 damin权限 不能访问。 库里加了 权限后,重新获取 token,即可访问
i'm foo, 17a22db1-834f-419b-b4b5-0607d7bdfcfc //UUID
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。