springBoot SpringSecurity集成jwt实现单点登录

文章目录

  • 前言
    • 1. JSON Web Token是什么
    • 2. 什么时候你应该用JSON Web Token
    • 3. JSON Web Token的结构是什么样的
  • 项目结构
  • 一、添加maven依赖
  • 二、服务端配置类以及处理类创建
    • 1. 创建SecurityConfig配置类
    • 2.AuthorizationServerConfig授权服务器配置
    • 3. ResourceServerConfig 资源服务器配置
    • 4. JwtTokenStoreConfig 配置类
    • 5. JwtTokenEnhancer jwt内容增强器配置
    • 6. 自定义user
    • 7.UserServiceImpl 自定义登录逻辑
    • 8. UserController测试类创建
  • 二、客服端简单搭建
    • 1. 添加maven依赖
    • 2. 启动类添加注解@EnableOAuth2Sso
    • 3. 创建测试类获取当前用户
    • 4. 先启动授权服务器,再启动客户端

前言

1. JSON Web Token是什么

JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

2. 什么时候你应该用JSON Web Token

下列场景中使用JSON Web Token是很有用的:
Authorization (授权) : 这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用的JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。
Information Exchange (信息交换) : 对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWT可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

3. JSON Web Token的结构是什么样的

JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:

Header(头部)
Payload(荷载)
Signature(签名)
因此,一个典型的JWT看起来是这个样子的:

xxxxx.yyyyy.zzzzz
接下来,具体看一下每一部分:

Header header典型的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。
例如:

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

然后,用Base64对这个JSON编码就得到JWT的第一部分

Payload JWT的第二部分是payload,它包含声明(要求)。声明是关于实体(通常是用户)和其他数据的声明。声明有三种类型: registered, public 和 private。
Registered claims : 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss (issuer), exp (expiration time), sub (subject), aud (audience)等。
Public claims : 可以随意定义。
Private claims : 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。 下面是一个例子:

{
    "sub": '1234567890',
    "name": 'john',
    "admin":true
}

对payload进行Base64编码就得到JWT的第二部分

注意,不要在JWT的payload或header中放置敏感信息,除非它们是加密的。

Signature
为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然对它们签名即可。
例如:

HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), secret)
签名是用于验证消息在传递过程中有没有被更改,并且,对于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。

看一张官网的图就明白了:
springBoot SpringSecurity集成jwt实现单点登录_第1张图片



项目结构

springBoot SpringSecurity集成jwt实现单点登录_第2张图片

一、添加maven依赖

<properties>
        <java.version>1.8java.version>
        <spring-cloud.verson>Greenwich.SR2spring-cloud.verson>
    properties>
	      <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-oauth2artifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-securityartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>
        
        <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
            <version>0.9.1version>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>${spring-cloud.verson}version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

二、服务端配置类以及处理类创建

1. 创建SecurityConfig配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author wanglei
 * @date 2021年05月15日 17:54
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable()
                //授权认证
                .authorizeRequests()
                //配置不被拦截的资源
                .antMatchers("/oauth/**,/login/**,logout/**").permitAll()
                //所有请求都被拦截,必须登录后才能访问
                .anyRequest().authenticated()
                .and()
                //表单提交
                .formLogin()
                .permitAll()
                ;

    }

2.AuthorizationServerConfig授权服务器配置

import com.example.demo.service.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
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.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.ArrayList;
import java.util.List;

/**
 * 授权服务器配置
 * @author wanglei
 * @date 2021年05月15日 18:08
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserServiceImpl userService;

    @Autowired
    @Qualifier("jwtTokenStore")
    private TokenStore tokenStore;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private  JwtTokenEnhancer jwtTokenEnhancer;

    /**
     * 使用密码模式所需配置
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //配置jwt内容增强器
        TokenEnhancerChain tokenEnhancerChain =new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(jwtAccessTokenConverter);
        tokenEnhancerChain.setTokenEnhancers(delegates);


        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userService)
                //配置储存令牌策略
                .tokenStore(tokenStore)
                .accessTokenConverter(jwtAccessTokenConverter)
                .tokenEnhancer(tokenEnhancerChain);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-Id
                .withClient("admin")
                //配置secret
                .secret(passwordEncoder.encode("112233"))
                //配置访问token的有效期
                .accessTokenValiditySeconds(3600)
                //配置刷新令牌token的有效期
                .refreshTokenValiditySeconds(864000)
                //redirect_uri用于授权成功后的跳转
                .redirectUris("http://www.baidu.com")
                //自动授权配置
                .autoApprove(true)
                //配置申请的权限范围
                .scopes("all")
                //配置authorizedGrantTypes,表示授权的类型
                // authorization_code 授权码模式授权,password 密码模式授权 refresh_token 刷新token
                .authorizedGrantTypes("password","refresh_token","authorization_code")

        ;
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //获取密钥需要身份认证,使用单点登录必须要配置
        security.tokenKeyAccess("isAuthenticated()");
    }
}

3. ResourceServerConfig 资源服务器配置

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;

/**
 * 资源服务器配置
 * @author wanglei
 * @date 2021年05月15日 18:17
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        //授权认证
        http.authorizeRequests()
                //所有请求都被拦截,必须授权后才能访问
                .anyRequest().authenticated()
                .and()
                .requestMatchers()
                //配置不被拦截的资源
                .antMatchers("/user/**");
    }
}

4. JwtTokenStoreConfig 配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;

/**
 * jwtToken配置类
 * @author wanglei
 * @date 2021年05月16日 12:02
 */
@Configuration
public class JwtTokenStoreConfig {
    @Bean
    public TokenStore jwtTokenStore(){
        return new JwtTokenStore(jwtAccessTokenConverter());
    }
	
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter jwtAccessTokenConverter =new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("test_key");
        return jwtAccessTokenConverter;
    }

    @Bean
    public JwtTokenEnhancer tokenEnhancer(){
        return new JwtTokenEnhancer();
    }
}

5. JwtTokenEnhancer jwt内容增强器配置

可以在jwt中添加你想要添加内容

import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;

import java.util.HashMap;
import java.util.Map;

/**
 * jwt内容增强器
 * @author wanglei
 * @date 2021年05月16日 12:13
 */
public class JwtTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
        Map<String,Object> info =new HashMap<>();
        info.put("enhance","enhance Info");
        ((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(info);
        return oAuth2AccessToken;
    }
}

6. 自定义user

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.List;

/**
 * http://localhost:8080/oauth/authorize?response_type=code&client_id=admin&redirect_uri=http://www.baidu.com&scope=all
 *
 * @author wanglei
 * @date 2021年05月15日 17:56
 */
public class User implements UserDetails {

    private String username;
    private String password;
    private List<GrantedAuthority> authorities;

    public User(String username,String password,List<GrantedAuthority> authorities){
        this.username=username;
        this.password=password;
        this.authorities=authorities;
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

7.UserServiceImpl 自定义登录逻辑

import com.example.demo.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

/**
 * 自定义登录逻辑
 * @author wanglei
 * @date 2021年05月15日 17:53
 */
@Service
public class UserServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //在数据库查出来
        String password =passwordEncoder.encode("123456");
        return new User("admin",password, AuthorityUtils.
                //设置权限
                commaSeparatedStringToAuthorityList("admin"));
    }


}

8. UserController测试类创建

测试获取当前用户信息

import io.jsonwebtoken.Jwts;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;

/**
 * @author wanglei
 * @date 2021年05月15日 18:20
 */
@RequestMapping("/user")
@RestController
public class UserController {

    /**
     * 获取当前用户
     * @param authentication
     * @return
     */
    @RequestMapping("/getUser")
    public Object getUser(Authentication authentication, HttpServletRequest request){
        String header = request.getHeader("Authorization");
        String token = header.substring(header.indexOf("bearer") + 7);
        return Jwts.parser()
                .setSigningKey("test_key".getBytes(StandardCharsets.UTF_8))
                .parseClaimsJws(token)
                .getBody();
    }
}

二、客服端简单搭建

1. 添加maven依赖

<properties>
        <java.version>1.8java.version>
        <spring-cloud.verson>Greenwich.SR2spring-cloud.verson>
    properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-oauth2artifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-securityartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starterartifactId>
        dependency>

        
        <dependency>
            <groupId>io.jsonwebtokengroupId>
            <artifactId>jjwtartifactId>
            <version>0.9.1version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>${spring-cloud.verson}version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

2. 启动类添加注解@EnableOAuth2Sso

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;

@SpringBootApplication
//开启单点登录
@EnableOAuth2Sso
public class Demo1Application {

	public static void main(String[] args) {
		SpringApplication.run(Demo1Application.class, args);
	}

}

3. 创建测试类获取当前用户

import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author wanglei
 * @date 2021年05月16日 13:19
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @RequestMapping("getUser")
    public Object getUser(Authentication authentication){
        return authentication;
    }
}

4. 先启动授权服务器,再启动客户端

在浏览器 输入 http://localhost:8081/user/getUser,会跳转到登录页面
springBoot SpringSecurity集成jwt实现单点登录_第3张图片

输入用户名和密码,用户名和密码是在登录逻辑中UserServiceImpl配置的
点击登录后成功跳转拿到用户信息
springBoot SpringSecurity集成jwt实现单点登录_第4张图片

{
	"authorities": [{
		"authority": "admin"
	}],
	"details": {
		"remoteAddress": "0:0:0:0:0:0:0:1",
		"sessionId": "B7D3DF74D2201D82A93EC9007079BE29",
		"tokenValue": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjExNzQzNTAsImF1dGhvcml0aWVzIjpbImFkbWluIl0sImp0aSI6Ijg1Mjg1OGZmLThkM2YtNDliNC04MjFiLWNhMGE1MjgyYTg5NCIsImNsaWVudF9pZCI6ImFkbWluIiwiZW5oYW5jZSI6ImVuaGFuY2UgSW5mbyJ9.fYPbZ96Y_Pw_t_VrravjtTCw1a1GdvmREtqApGBTIJQ",
		"tokenType": "bearer",
		"decodedDetails": null
	},
	"authenticated": true,
	"userAuthentication": {
		"authorities": [{
			"authority": "admin"
		}],
		"details": null,
		"authenticated": true,
		"principal": "admin",
		"credentials": "N/A",
		"name": "admin"
	},
	"credentials": "",
	"principal": "admin",
	"clientOnly": false,
	"oauth2Request": {
		"clientId": "admin",
		"scope": ["all"],
		"requestParameters": {
			"client_id": "admin"
		},
		"resourceIds": [],
		"authorities": [],
		"approved": true,
		"refresh": false,
		"redirectUri": null,
		"responseTypes": [],
		"extensions": {},
		"grantType": null,
		"refreshTokenRequest": null
	},
	"name": "admin"
}

你可能感兴趣的:(jwt,spring,java,spring,boot)