zoukankan      html  css  js  c++  java
  • 使用Spring Cloud OAuth2和JWT保护微服务

    采用Spring Security AOuth2 和 JWT 的方式,避免每次请求都需要远程调度 Uaa 服务。采用Spring Security OAuth2 和 JWT 的方式,Uaa 服务只验证一次,返回JWT。返回的 JWT 包含了用户的所有信息,包括权限信息。

    1.什么是JWT?#

    JSON Web Token(JWT)是一种开放的标准(RFC 7519),JWT定义了一种紧凑且自包含的标准,该标准旨在将各个主体的信息包装为 JSON 对象。主体信息是通过数字签名进行加密和验证的。常使用 HMAC 算法或 RSA(公钥/私钥的非对称性加密)算法对JWT进行签名,安全性很高。

    JWT 特点:

    • 紧凑型:数据体积小,可通过 POST 请求参数或 HTTP 请求头发送。
    • 自包含:JWT包含了主体的所有信息,避免了每个请求都需要向Uaa服务验证身份,降低了服务器的负载。

    2.JWT的结构#

    JWT结构:

    • Header(头)
    • Payload(有效载荷)
    • Signature(签名)

    因此,JWT的通常格式是:xxxxx.yyyyy.zzzzz

    (1)Header

    Header 通常是由两部分组成:令牌的类型(即JWT)和使用的算法类型,如 HMAC、SHA256和RSA。例如:

    Copy
    { "typ": "JWT", "alg": "HS256" }

    将 Header 用 Base64 编码作为 JWT 的第一部分。

    (2)Payload

    这是 JWT 的第二部分,包含了用户的一些信息和Claim(声明、权利)。有3类型的 Claim:保留、公开和私人。

    Copy
    { "sub": "123456789", "name": "John Doe", "admin": true }

    将 Payload 用 Base64 编码作为 JWT 的第一部分。

    (3)Signature

    要创建签名部分,需要将 Base64 编码后的 Header、Payload 和秘钥进行签名,一个典型的格式如下:

    Copy
    HMACSHA256( base64UrlEncode(header) + '.' + base64UrlEncode(payload), secret )

    3.如何使用JWT#

    认证流程图如下,客户端获取JWT后,以后每次请求都不需要再通过Uaa服务来判断该请求的用户以及该用户的权限。在微服务中,可以利用JWT实现单点登录。

    4.案例工程架构#

    三个工程:

    • eureka-server:注册服务中心,端口8761。这里不再演示搭建。
    • auth-service:负责授权,授权需要用户提供客户端的 clientId 和 password,以及授权用户的username和password。这些信息准备无误之后,auth-service 返回JWT,该 JWT 包含了用户的基本信息和权限点信息,并通过 RSA 加密。
    • user-service:作为资源服务,它的资源以及被保护起来了,需要相应的权限才能访问。user-service 服务得到用户请求的 JWT 后,先通过公钥解密JWT,得到该JWT对应的用户的信息和用户的权限信息,再判断该用户是否有权限访问该资源。

    工程架构图:

    5.构建auth-service工程#

    1.新建Spring Boot工程,取名为 auth-service,其完整pom.xml文件为.

    Copy
    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
    <span class="hljs-tag">&lt;<span class="hljs-name">parent</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>1.5.3.RELEASE<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">relativePath</span>/&gt;</span> <span class="hljs-comment">&lt;!-- lookup parent from repository --&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">parent</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>auth-service<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>auth-service<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">description</span>&gt;</span>Demo project for Spring Boot<span class="hljs-tag">&lt;/<span class="hljs-name">description</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">java.version</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">java.version</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">spring-cloud.version</span>&gt;</span>Dalston.SR1<span class="hljs-tag">&lt;/<span class="hljs-name">spring-cloud.version</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-starter-eureka<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.security<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-security-jwt<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.security.oauth<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-security-oauth2<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-data-jpa<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>mysql<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mysql-connector-java<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencyManagement</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-dependencies<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>${spring-cloud.version}<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">type</span>&gt;</span>pom<span class="hljs-tag">&lt;/<span class="hljs-name">type</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>import<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencyManagement</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
            <span class="hljs-comment">&lt;!--防止jks文件被mavne编译导致不可用--&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.apache.maven.plugins<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>maven-resources-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">configuration</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">nonFilteredFileExtensions</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">nonFilteredFileExtension</span>&gt;</span>cert<span class="hljs-tag">&lt;/<span class="hljs-name">nonFilteredFileExtension</span>&gt;</span>
                        <span class="hljs-tag">&lt;<span class="hljs-name">nonFilteredFileExtension</span>&gt;</span>jks<span class="hljs-tag">&lt;/<span class="hljs-name">nonFilteredFileExtension</span>&gt;</span>
                    <span class="hljs-tag">&lt;/<span class="hljs-name">nonFilteredFileExtensions</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">configuration</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>
    

    </project>

    2.配置application.yml文件

    Copy
    spring: application: name: auth-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 server: port: 9999 eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/

    3.配置Spring Security

    Copy
    @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    <span class="hljs-meta">@Override</span>
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> AuthenticationManager <span class="hljs-title">authenticationManagerBean</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">super</span>.authenticationManagerBean();
    }
    
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        http
            .csrf().disable() <span class="hljs-comment">//关闭CSRF</span>
                .exceptionHandling()
                .authenticationEntryPoint((request, response, authException) -&gt; response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
            .and()
                .authorizeRequests()
                .antMatchers(<span class="hljs-string">"/**"</span>).authenticated()
            .and()
                .httpBasic();
    }
    
    <span class="hljs-meta">@Autowired</span>
    UserServiceDetail userServiceDetail;
    
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">(AuthenticationManagerBuilder auth)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        auth.userDetailsService(userServiceDetail)
                .passwordEncoder(<span class="hljs-keyword">new</span> BCryptPasswordEncoder()); <span class="hljs-comment">//密码加密</span>
    }
    

    }

    UserServiceDetail.java

    Copy
    @Service public class UserServiceDetail implements UserDetailsService { @Autowired private UserDao userRepository;
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> UserDetails <span class="hljs-title">loadUserByUsername</span><span class="hljs-params">(String username)</span> <span class="hljs-keyword">throws</span> UsernameNotFoundException </span>{
        <span class="hljs-keyword">return</span> userRepository.findByUsername(username);
    }
    

    }

    UserDao.java

    Copy
    @Repository public interface UserDao extends JpaRepository<User, Long> {
    <span class="hljs-function">User <span class="hljs-title">findByUsername</span><span class="hljs-params">(String username)</span></span>;
    

    }

    User对象和上一篇文章的内容一样,需要实现UserDetails接口,Role对象需要实现GrantedAuthority接口.

    Copy
    @Entity public class User implements UserDetails, Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
    <span class="hljs-meta">@Column</span>(nullable = <span class="hljs-keyword">false</span>,  unique = <span class="hljs-keyword">true</span>)
    <span class="hljs-keyword">private</span> String username;
    
    <span class="hljs-meta">@Column</span>
    <span class="hljs-keyword">private</span> String password;
    
    <span class="hljs-meta">@ManyToMany</span>(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    <span class="hljs-meta">@JoinTable</span>(name = <span class="hljs-string">"user_role"</span>, joinColumns = <span class="hljs-meta">@JoinColumn</span>(name = <span class="hljs-string">"user_id"</span>, referencedColumnName = <span class="hljs-string">"id"</span>),
            inverseJoinColumns = <span class="hljs-meta">@JoinColumn</span>(name = <span class="hljs-string">"role_id"</span>, referencedColumnName = <span class="hljs-string">"id"</span>))
    <span class="hljs-keyword">private</span> List&lt;Role&gt; authorities;
    
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">User</span><span class="hljs-params">()</span> </span>{
    }
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> Long <span class="hljs-title">getId</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> id;
    }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setId</span><span class="hljs-params">(Long id)</span> </span>{
        <span class="hljs-keyword">this</span>.id = id;
    }
    
    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> Collection&lt;? extends GrantedAuthority&gt; getAuthorities() {
        <span class="hljs-keyword">return</span> authorities;
    }
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setAuthorities</span><span class="hljs-params">(List&lt;Role&gt; authorities)</span> </span>{
        <span class="hljs-keyword">this</span>.authorities = authorities;
    }
    
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getUsername</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> username;
    }
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setUsername</span><span class="hljs-params">(String username)</span> </span>{
        <span class="hljs-keyword">this</span>.username = username;
    }
    
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getPassword</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> password;
    }
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setPassword</span><span class="hljs-params">(String password)</span> </span>{
        <span class="hljs-keyword">this</span>.password = password;
    }
    
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isAccountNonExpired</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }
    
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isAccountNonLocked</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }
    
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isCredentialsNonExpired</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }
    
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isEnabled</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    }
    

    }

    Copy
    @Entity public class Role implements GrantedAuthority { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public String getAuthority() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return name; } }

    4.配置 Authorization Server

    在 OAuth2Config 这个类中配置 AuthorizationServer,其代码如下:

    Copy
    @Configuration @EnableAuthorizationServer public class OAuth2Config extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() //将客户端的信息存储在内存中 .withClient("user-service") //创建了一个Client为"user-service"的客户端 .secret("123456") .scopes("service") //客户端的域 .authorizedGrantTypes("refresh_token", "password") //配置类验证类型为 refresh_token和password .accessTokenValiditySeconds(12*300); //5min过期 }
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">(AuthorizationServerEndpointsConfigurer endpoints)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        endpoints.tokenStore(tokenStore()).tokenEnhancer(jwtTokenEnhancer()).authenticationManager(authenticationManager);
    }
    
    <span class="hljs-meta">@Autowired</span>
    <span class="hljs-meta">@Qualifier</span>(<span class="hljs-string">"authenticationManagerBean"</span>)
    <span class="hljs-keyword">private</span> AuthenticationManager authenticationManager;
    
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> TokenStore <span class="hljs-title">tokenStore</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> JwtTokenStore(jwtTokenEnhancer());
    }
    
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> JwtAccessTokenConverter <span class="hljs-title">jwtTokenEnhancer</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">//注意此处需要相应的jks文件</span>
        KeyStoreKeyFactory keyStoreKeyFactory = <span class="hljs-keyword">new</span> KeyStoreKeyFactory(<span class="hljs-keyword">new</span> ClassPathResource(<span class="hljs-string">"fzp-jwt.jks"</span>), <span class="hljs-string">"fzp123"</span>.toCharArray());
        JwtAccessTokenConverter converter = <span class="hljs-keyword">new</span> JwtAccessTokenConverter();
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair(<span class="hljs-string">"fzp-jwt"</span>));
        <span class="hljs-keyword">return</span> converter;
    }
    

    }

    5.生成 jks 文件

    配置 JwtTokenStore 时需要使用 jks 文件作为 Token 加密的秘钥。

    jks 文件需要Java keytool工具,保证Java环境变量没问题,打开计算机终端,输入命令:

    keytool -genkeypair -alias fzp-jwt -validity 3650 -keyalg RSA -dname "CN=jwt,OU=jtw,O=jwt,L=zurich,S=zurich,C=CH" -keypass fzp123 -keystore fzp-jwt.jks -storepass fzp123

    解释,-alias 选项为别名,-keypass 和 -storepass 为密码选项,-validity 为配置jks文件过期时间(单位:天)。

    获取的 jks 文件作为私钥,只允许 Uaa 服务持有,并用作加密 JWT。也就是把生成的 jks 文件放到 auth-service 工程的resource目录下。那么 user-service 这样的资源服务,是如何解密 JWT 的呢?这时就需要使用 jks 文件的公钥。获取 jks 文件的公钥命令如下:

    keytool -list -rfc --keystore fzp-jwt.jks | openssl x509 -inform pem -pubkey

    这个命令要求你的计算机上安装了openSSL(下载地址),然后手动把安装的openssl.exe所在目录配置到环境变量。

    输入密码fzp123后,显示的信息很多,我们只提取 PUBLIC KEY,即如下所示:

    -----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlCFiWbZXIb5kwEaHjW+/
    7J4b+KzXZffRl5RJ9rAMgfRXHqGG8RM2Dlf95JwTXzerY6igUq7FVgFjnPbexVt3
    vKKyjdy2gBuOaXqaYJEZSfuKCNN/WbOF8e7ny4fLMFilbhpzoqkSHiR+nAHLkYct
    OnOKMPK1SwmvkNMn3aTEJHhxGh1RlWbMAAQ+QLI2D7zCzQ7Uh3F+Kw0pd2gBYd8W
    +DKTn1Tprugdykirr6u0p66yK5f1T9O+LEaJa8FjtLF66siBdGRaNYMExNi21lJk
    i5dD3ViVBIVKi9ZaTsK9Sxa3dOX1aE5Zd5A9cPsBIZ12spYgemfj6DjOw6lk7jkG
    9QIDAQAB
    -----END PUBLIC KEY-----

    新建一个 public.cert 文件,将上面的公钥信息复制到 public.cert 文件中并保存。并将文件放到 user-service 等资源服务的resources目录下。到目前为止,Uaa 服务已经搭建完毕。

    需要注意的是,Maven 在项目编译时,可能会将 jks 文件编译,导致 jks 文件乱码,最后不可用。需要在工程的 pom 文件中添加以下内容:

    Copy
    <!--防止jks文件被mavne编译导致不可用--> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <configuration> <nonFilteredFileExtensions> <nonFilteredFileExtension>cert</nonFilteredFileExtension> <nonFilteredFileExtension>jks</nonFilteredFileExtension> </nonFilteredFileExtensions> </configuration> </plugin>

    最后,别忘了在启动类注解@EnableEurekaClient开启服务注册.

    Copy
    @SpringBootApplication @EnableEurekaClient public class AuthServiceApplication { public static void main(String[] args) { SpringApplication.run(AuthServiceApplication.class, args); } }

    6.构建user-service资源服务#

    1.新建Spring Boot工程,取名为user-service,其完整pom.xml文件:

    Copy
    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>com.example<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>user-service<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>user-service<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">description</span>&gt;</span>Demo project for Spring Boot<span class="hljs-tag">&lt;/<span class="hljs-name">description</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">properties</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">java.version</span>&gt;</span>1.8<span class="hljs-tag">&lt;/<span class="hljs-name">java.version</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">spring-cloud.version</span>&gt;</span>Dalston.SR1<span class="hljs-tag">&lt;/<span class="hljs-name">spring-cloud.version</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">properties</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-starter-eureka<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-web<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.security.oauth<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-security-oauth2<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-data-jpa<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>mysql<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>mysql-connector-java<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-starter-hystrix<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-starter-feign<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    
        <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>test<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">dependencyManagement</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">dependencies</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.cloud<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-cloud-dependencies<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>${spring-cloud.version}<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">type</span>&gt;</span>pom<span class="hljs-tag">&lt;/<span class="hljs-name">type</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">scope</span>&gt;</span>import<span class="hljs-tag">&lt;/<span class="hljs-name">scope</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">dependencies</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">dependencyManagement</span>&gt;</span>
    
    <span class="hljs-tag">&lt;<span class="hljs-name">build</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">plugins</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">plugin</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.springframework.boot<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">plugin</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">plugins</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">build</span>&gt;</span>
    

    </project>

    2.配置文件application.yml

    在工程的配置文件application.yml中,配置程序名为 user-service,端口号为 9090,另外,需要配置 feign.hystrix.enable 为true,即开启 Feign 的 Hystrix 功能。完整的配置代码如下:

    Copy
    server: port: 9090 eureka: client: service-url: 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

    3.配置Resource Server

    在配置Resource Server之前,需要注入 JwtTokenStore 类型的 Bean。

    Copy
    @Configuration public class JwtConfig { @Autowired JwtAccessTokenConverter jwtAccessTokenConverter;
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-meta">@Qualifier</span>(<span class="hljs-string">"tokenStore"</span>)
    <span class="hljs-function"><span class="hljs-keyword">public</span> TokenStore <span class="hljs-title">tokenStore</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> JwtTokenStore(jwtAccessTokenConverter);
    }
    
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> JwtAccessTokenConverter <span class="hljs-title">jwtTokenEnhancer</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">//用作 JWT 转换器</span>
        JwtAccessTokenConverter converter =  <span class="hljs-keyword">new</span> JwtAccessTokenConverter();
        Resource resource = <span class="hljs-keyword">new</span> ClassPathResource(<span class="hljs-string">"public.cert"</span>);
        String publicKey ;
        <span class="hljs-keyword">try</span> {
            publicKey = <span class="hljs-keyword">new</span> String(FileCopyUtils.copyToByteArray(resource.getInputStream()));
        } <span class="hljs-keyword">catch</span> (IOException e) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(e);
        }
        converter.setVerifierKey(publicKey); <span class="hljs-comment">//设置公钥</span>
        <span class="hljs-keyword">return</span> converter;
    }
    

    }

    然后配置 Resource Server

    Copy
    @Configuration @EnableResourceServer //开启Resource Server功能 public class ResourceServerConfig extends ResourceServerConfigurerAdapter{ @Autowired TokenStore tokenStore;
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers(<span class="hljs-string">"/user/login"</span>,<span class="hljs-string">"/user/register"</span>).permitAll()
                .antMatchers(<span class="hljs-string">"/**"</span>).authenticated();
    
    }
    
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">(ResourceServerSecurityConfigurer resources)</span> <span class="hljs-keyword">throws</span> Exception </span>{
        resources.tokenStore(tokenStore);
    }
    

    }

    4.新建一个配置类 GlobalMethodSecurityConfig,在此类中通过 @EnableGlobalMethodSecurity(prePostEnabled = true)注解开启方法级别的安全验证。

    Copy
    @Configuration @EnableGlobalMethodSecurity(prePostEnabled = true) public class GlobalMethodSecurityConfig {

    }

    5.编写用户注册接口

    拷贝auth-service工程的User.java、Role.java 和 UserDao.java 到本工程。

    在 Service 层的 UserService 写一个插入用户的方法,代码如下

    Copy
    @Service public class UserServiceDetail {
    <span class="hljs-meta">@Autowired</span>
    <span class="hljs-keyword">private</span> UserDao userRepository;
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">insertUser</span><span class="hljs-params">(String username,String  password)</span></span>{
        User user=<span class="hljs-keyword">new</span> User();
        user.setUsername(username);
        user.setPassword(BPwdEncoderUtil.BCryptPassword(password));
        <span class="hljs-keyword">return</span> userRepository.save(user);
    }
    

    }

    BPwdEncoderUtil工具类

    Copy
    public class BPwdEncoderUtil {
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> BCryptPasswordEncoder encoder = <span class="hljs-keyword">new</span> BCryptPasswordEncoder();
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String  <span class="hljs-title">BCryptPassword</span><span class="hljs-params">(String password)</span></span>{
        <span class="hljs-keyword">return</span> encoder.encode(password);
    }
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">matches</span><span class="hljs-params">(CharSequence rawPassword, String encodedPassword)</span></span>{
        <span class="hljs-keyword">return</span> encoder.matches(rawPassword,encodedPassword);
    }
    

    }

    在 Web 层,在 Controller 中写一个注册的 API 接口 “/user/register”,代码如下

    Copy
    @RestController @RequestMapping("/user") public class UserController { @Autowired UserServiceDetail userServiceDetail;
    <span class="hljs-meta">@PostMapping</span>(<span class="hljs-string">"/register"</span>)
    <span class="hljs-function"><span class="hljs-keyword">public</span> User <span class="hljs-title">postUser</span><span class="hljs-params">(@RequestParam(<span class="hljs-string">"username"</span>)</span> String username , @<span class="hljs-title">RequestParam</span><span class="hljs-params">(<span class="hljs-string">"password"</span>)</span> String password)</span>{
        <span class="hljs-comment">//参数判断,省略</span>
       <span class="hljs-keyword">return</span> userServiceDetail.insertUser(username,password);
    }
    

    }

    6.编写用户登录接口

    在Service层,在 UserServiceDetail 中添加一个 login(登录)方法,代码如下:

    Copy
    @Service public class UserServiceDetail {
    <span class="hljs-meta">@Autowired</span>
    <span class="hljs-keyword">private</span> AuthServiceClient client;
    
    <span class="hljs-function"><span class="hljs-keyword">public</span> UserLoginDTO <span class="hljs-title">login</span><span class="hljs-params">(String username, String password)</span></span>{
        User user=userRepository.findByUsername(username);
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">null</span> == user) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UserLoginException(<span class="hljs-string">"error username"</span>);
        }
        <span class="hljs-keyword">if</span>(!BPwdEncoderUtil.matches(password,user.getPassword())){
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UserLoginException(<span class="hljs-string">"error password"</span>);
        }
        <span class="hljs-comment">// 获取token</span>
        JWT jwt=client.getToken(<span class="hljs-string">"Basic dXNlci1zZXJ2aWNlOjEyMzQ1Ng=="</span>,<span class="hljs-string">"password"</span>,username,password);
        <span class="hljs-comment">// 获得用户菜单</span>
        <span class="hljs-keyword">if</span>(jwt==<span class="hljs-keyword">null</span>){
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> UserLoginException(<span class="hljs-string">"error internal"</span>);
        }
        UserLoginDTO userLoginDTO=<span class="hljs-keyword">new</span> UserLoginDTO();
        userLoginDTO.setJwt(jwt);
        userLoginDTO.setUser(user);
        <span class="hljs-keyword">return</span> userLoginDTO;
    
    }
    

    }

    AuthServiceClient 通过向 auth-service 服务远程调用“/oauth/token” API接口,获取 JWT。在 "/oauth/token" API 接口,获取JWT。在“/oauth/token”API接口中需要在请求头传入 Authorization 信息,并需要传请求参数认证类型 grant_type、用户名 username 和密码 password,代码如下:

    Copy
    @FeignClient(value = "auth-service",fallback =AuthServiceHystrix.class ) public interface AuthServiceClient {
    <span class="hljs-meta">@PostMapping</span>(value = <span class="hljs-string">"/oauth/token"</span>)
    <span class="hljs-function">JWT <span class="hljs-title">getToken</span><span class="hljs-params">(@RequestHeader(value = <span class="hljs-string">"Authorization"</span>)</span> String authorization, @<span class="hljs-title">RequestParam</span><span class="hljs-params">(<span class="hljs-string">"grant_type"</span>)</span> String type,
                 @<span class="hljs-title">RequestParam</span><span class="hljs-params">(<span class="hljs-string">"username"</span>)</span> String username, @<span class="hljs-title">RequestParam</span><span class="hljs-params">(<span class="hljs-string">"password"</span>)</span> String password)</span>;
    

    }

    其中,AuthServiceHystrix 为AuthServiceClient 的熔断器,代码如下:

    Copy
    @Component public class AuthServiceHystrix implements AuthServiceClient { @Override public JWT getToken(String authorization, String type, String username, String password) { return null; } }

    JWT 为一个 JavaBean,它包含了 access_token、token_type 和 refresh_token 等信息,代码如下:

    Copy
    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; //getter setter

    UserLoginDTO 包含了一个 User 和一个 JWT 对象,用于返回数据的实体:

    Copy
    public class UserLoginDTO { private JWT jwt; private User user; //setter getter }

    登录异常类 UserLoginException

    Copy
    public class UserLoginException extends RuntimeException{ public UserLoginException(String message) { super(message); } }

    统一异常处理

    Copy
    @ControllerAdvice @ResponseBody public class ExceptionHandle { @ExceptionHandler(UserLoginException.class) public ResponseEntity<String> handleException(Exception e) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ResponseEntity(e.getMessage(), HttpStatus.OK);
    }
    

    }

    在web层的 UserController 类补充一个登录的API接口“/user/login”.

    Copy
    @PostMapping("/login") public UserLoginDTO login(@RequestParam("username") String username , @RequestParam("password") String password){ //参数判断,省略 return userServiceDetail.login(username,password); }

    为了测试权限,再补充一个"/foo"接口,该接口需要“ROLE_ADMIN”权限.

    Copy
    @RequestMapping(value = "/foo", method = RequestMethod.GET) @PreAuthorize("hasAuthority('ROLE_ADMIN')") public String getFoo() { return "i'm foo, " + UUID.randomUUID().toString(); }

    最后,在启动类注解开启Feign:

    Copy
    @SpringBootApplication @EnableFeignClients @EnableEurekaClient public class UserServiceApplication { public static void main(String[] args) { SpringApplication.run(UserServiceApplication.class, args); } }

    7.启动工程,开始测试#

    经过千辛万苦,终于搭建了一个Demo工程,现在开始依次启动 eureka-server、auth-service 和 user-service工程。这里我们使用PostMan测试编写的接口。

    1.注册用户

    2.登录获取Token

    3.访问/user/foo

    复制 access_token到 Header头部,发起GET请求。

    Copy
    "Authorization":"Bearer {access_token}"

    因为没有权限,访问被拒绝,我们手动在数据库添加"ROLE_ADMIN"权限,并与该用户关联。重新登录并获取Token,重新请求“/user/foo”接口

    总结#

    在本案例中,用户通过登录接口来获取授权服务的Token 。用户获取Token 成功后,在以后每次访问资源服务的请求中都需要携带该Token 。资源服务通过公钥解密Token ,解密成功后可以获取用户信息和权限信息,从而判断该Token 所对应的用户是谁, 具有什么权限。
    这个架构的优点在于,一次获取Token , 多次使用,不再每次询问Uaa 服务该Token 所对应的用户信息和用户的权限信息。这个架构也有缺点,例如一旦用户的权限发生了改变, 该Token 中存储的权限信息并没有改变, 需要重新登录获取新的Token 。就算重新获取了Token,如果原来的Token 没有过期,仍然是可以使用的,所以需要根据具体的业务场景来设置Token的过期时间。一种改进方式是将登录成功后获取的Token 缓存在网关上,如果用户的权限更改,将网关上缓存的Token 删除。当请求经过网关,判断请求的Token 在缓存中是否存在,如果缓存中不存在该Token ,则提示用户重新登录。

    参考:《深入理解Spring Cloud与微服务构建》方志朋

    JWT返回null问题解决

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient("client")
                    .redirectUris("http://www.baidu.com")
                    //此处的scopes是无用的,可以随意设置
                    .scopes("all","read", "write")
                    .secret(passwordEncoder.encode("secret"))//这块就是解决办法,原本secret换成passwordEncoder.encode("secret")
                    .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                    //有效期时间
    //                .accessTokenValiditySeconds(accessTokenValiditySeconds)
                    //刷新事件
    //                .refreshTokenValiditySeconds(refreshTokenValiditySeconds)
                    .and()
                    .withClient("webapp")
                    .scopes("xx")
                    .authorizedGrantTypes("implicit");
        }
  • 相关阅读:
    深入了解Java ClassLoader、Bytecode 、ASM、cglib (I)
    如何在ant里import
    敏捷练习(1)评估我的生活方向盘
    你是一个合格的孩子吗?
    [转] C# 路径(目录)
    XML学习记录
    js学习总结不断更新(1)
    LINQ TO XML练习
    做技术,切不可沉湎于技术
    js学习总结持续更新(2)
  • 原文地址:https://www.cnblogs.com/jpfss/p/11935560.html
Copyright © 2011-2022 走看看