基于 Spring Security OAuth2和 JWT 构建保护微服务系统 |
您所在的位置:网站首页 › oauth2和jwt › 基于 Spring Security OAuth2和 JWT 构建保护微服务系统 |
我们希望自己的微服务能够在用户登录之后才可以访问,而单独给每个微服务单独做用户权限模块就显得很弱了,从复用角度来说是需要重构的,从功能角度来说,也是欠缺的。尤其是前后端完全分离之后,我们的用户信息不一定存在于 Session 会话中,本节内容使用OAuth2+JWT的功能恰好能够弥补这块。 应用场景常见的应用场景如下图,用户通过浏览器进行登录,一旦确定用户名和密码正确,那么在服务器端使用秘钥创建 JWT,并且返回给浏览器;接下来我们的请求需要在头部增加 jwt 信息,服务器端进行解密获取用户信息,然后进行其他业务逻辑处理,再返回客户端 image 实战案例我们基于 Spring Cloud 的骨架进行搭建,分为3个工程,eureka 服务器,负责微服务注册;auth 服务器,负责授权,需要提供 clientId 和密码;user 微服务,一个微服务提供,他作为资源服务器,资源是被保护起来的,需要相应的权限才能访问。User 微服务得到用户请求的 JWT 之后,使用公钥解密,得到用户信息和权限信息。 image 编写主 maven 工程构建一个 maven 项目,打包类型是 pom,其中该 pom 文件内容如下 4.0.0 com.cnsesan cnsean-architecture-spring-cloud 0.0.1-SNAPSHOT pom org.springframework.boot spring-boot-starter-parent 1.5.13.RELEASE 1.0.0-SNAPSHOT 1.8 io.spring.platform platform-bom Brussels-SR11 pom import org.springframework.cloud spring-cloud-dependencies Dalston.SR5 pom import org.apache.maven.plugins maven-compiler-plugin 1.8 1.8 1.8 UTF-8 cnsesan-eureka-single cnsesan-uaa-service cnsesan-user-service上述的版本是经过测试可以正常使用的,如果需要更新到 SpringBoot2.0版本,需要更新其他版本进行对应。同时也看到该 pom 内部包含3个 module,接下来我们分别来构建这3个 module。 构建 EurekaServer这里我们构建的是单个 Eureka 服务器作为测试,真实环境是需要集群的。在父项目的基础上,右键构建,如下图(IDE 为 STS) image image 配置 pom,加入依赖 4.0.0 com.cnsesan cnsean-architecture-spring-cloud 0.0.1-SNAPSHOT cnsesan-eureka-single org.springframework.cloud spring-cloud-starter-eureka-server这里仅仅引入 eureka 服务器端的依赖即可 配置 yml 文件 spring: application: name: eureka-server eureka: instance: hostname: localhost client: serviceUrl: defaultZone: http://localhost:8762/eureka/ register-with-eureka: false fetch-registry: false# instance: # preferIpAddress: true server: # 关闭自我保护模式(缺省为打开) enable-self-preservation: false # 续期时间,即扫描失效服务的间隔时间(缺省为60*1000ms) eviction-interval-timer-in-ms: 5000logging: level: com.netflix: INFO server: port: 8762端口是8762,名称是eureka-server 在 Application 启动类中添加注解 package com.cnsesan.eureka;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.builder.SpringApplicationBuilder;import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@EnableEurekaServer@SpringBootApplicationpublic class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class) .web(true).run(args); } }找到BootDashboard,运行eureka image 构建 Uaa 授权服务同样构建 maven 项目,导入依赖,pom 文件为 4.0.0 com.cnsesan cnsean-architecture-spring-cloud 0.0.1-SNAPSHOT cnsesan-uaa-service org.springframework.cloud spring-cloud-starter-oauth2 org.springframework.boot spring-boot-starter-data-jpa mysql mysql-connector-java org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-eureka org.apache.maven.plugins maven-resources-plugin cert jks其中最后一段是防止打包的时候把公钥和私钥文件搞乱,读取不了。 接下来配置 application.yml 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: root jpa: hibernate: ddl-auto: update show-sql: trueserver: port: 9999eureka: client: serviceUrl: defaultZone: http://localhost:8762/eureka/端口是9999,服务名称是 uaa-service与 application.yml 相同地方还需要2个文件,分别是cnsesan-jwt.jks和 public.cert我们先把这两个文件弄出来 keytool -genkeypair -alias cnsesan-jwt -validity 3650 -keyalg RSA -dname "CN=jwt,OU=cnsesan,O=cnsesan,L=zurich,S=zurich,C=CH" -keypass cnsesan123 -keystore cnsesan-jwt.jks -storepass cnsesan123如上操作得到cnsesan-jwt.jks然后需要的都公钥文件,如下 keytool -list -rfc --keystore cnsesan-jwt.jks | openssl x509 -inform pem -pubkey输入密码 cnsesan123,将如下片段拷贝到新文件public.cert image 可以得到public.cert将这两个文件拷贝到 resource 目录下 image 接下来首先编写启动类,主要是几个注解 package com.cnsesan.uaa;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.builder.SpringApplicationBuilder;import org.springframework.cloud.netflix.eureka.EnableEurekaClient;import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;@SpringBootApplication@EnableResourceServer@EnableEurekaClientpublic class Application { public static void main(String[] args) { new SpringApplicationBuilder(Application.class) .web(true).run(args); } }然后是编写我们的配置类,也是最核心的地方 首先编写配置Spring Security @Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)public class WebSecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired private UserServiceDetail userServiceDetail ; @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .exceptionHandling() .authenticationEntryPoint((request,response,authException)->response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) .and() .authorizeRequests() .antMatchers("/**").authenticated() .and() .httpBasic() ; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userServiceDetail).passwordEncoder(new BCryptPasswordEncoder()); } @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }这里有个UserServiceDetail,实现了UserDetailsService,他的代码如下,主要是负责用户信息获取的 @Servicepublic class UserServiceDetail implements UserDetailsService { @Autowired private UserDao userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return userRepository.findByUsername(username); } }UserDao 类 是一个接口,使用 JPA 的方式,如下 public interface UserDao extends JpaRepository{ User findByUsername(String username); }User 和 Role 两个实体类需要做如下的实现 @Entitypublic class User implements UserDetails, Serializable{ private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; @Column(nullable=false,unique=true) private String username; @Column() private String password; @ManyToMany(cascade = CascadeType.ALL, fetch= FetchType.EAGER) @JoinTable(name="user_role",joinColumns=@JoinColumn(name="user_id",referencedColumnName="id"),inverseJoinColumns=@JoinColumn(name="role_id",referencedColumnName="id")) private List authorities; public User() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public void setAuthorities(List authorities) { this.authorities = authorities; } @Override public Collection |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |