基于 Spring Security OAuth2和 JWT 构建保护微服务系统 您所在的位置:网站首页 oauth2和jwt 基于 Spring Security OAuth2和 JWT 构建保护微服务系统

基于 Spring Security OAuth2和 JWT 构建保护微服务系统

2022-06-30 08:10| 来源: 网络整理| 查看: 265

我们希望自己的微服务能够在用户登录之后才可以访问,而单独给每个微服务单独做用户权限模块就显得很弱了,从复用角度来说是需要重构的,从功能角度来说,也是欠缺的。尤其是前后端完全分离之后,我们的用户信息不一定存在于 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 实验室设备网 版权所有