SpringSecurity+OAuth2.0+JWT实现单点登录应用

SpringSecurity+OAuth2.0+JWT实现单点登录应用
gitee项目练习地址:https://gitee.com/xzq25_com/springsecurity.oauth2

一、搭建OAuth授权服务器,采用授权码模式+JWT令牌

目录如下:
在这里插入图片描述

1,导入依赖:

   <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
        </dependency>
        
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
        </dependency>
        
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

2,配置AuthorizationServerConfig认证授权服务

/**
 * @author xzq
 * @description: 授权服务器
 * @date 2022/12/5 13:43
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private TokenStore jwtTokenStore;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;

    /**
     * @description: 使用密码模式所需配置
     * @author liyonghui
     * @date 2021/12/5 14:27
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        List<TokenEnhancer> delegates = new ArrayList<>();
        delegates.add(jwtTokenEnhancer);
        delegates.add(jwtAccessTokenConverter);

        //配置JWT内容增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(delegates);

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

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //配置client-id
                .withClient("super")
                //配置client-secret
                .secret(passwordEncoder.encode("xxx"))
                //配置刷新令牌的有效期
                .refreshTokenValiditySeconds(6000)
                //配置redirect-url,用于授权成功后跳转
                .redirectUris("http://localhost:8081/login","http://localhost:8082/login")
                //自动授权
                .autoApprove(true)
                //配置申请的权限范围
                .scopes("user","order","payment")
                //授权类型-使用授权码模式
                .authorizedGrantTypes("authorization_code","refresh_token");
    }

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

3,配置WebSecurityConfigurerAdapter

/**
 * @author xzq
 * @description: TODO
 * @date 2022/12/5 13:35
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/oauth/**", "/login/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll();
    }
}

4,配置JwtTokenStoreConfig

/**
 * @author xzq
 * @description: TODO
 * @date 2022/12/5 15:39
 */
@Configuration
public class JwtTokenStoreConfig {

    @Bean
    public TokenStore jwtTokenStore() {
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //配置JWT使用的秘钥
        jwtAccessTokenConverter.setSigningKey("test_key");
        return jwtAccessTokenConverter;
    }
}

5,配置JwtTokenEnhancer:Jwt增强

/**
 * @author liyonghui
 * @description: JWT内容增强
 * @date 2021/12/5 15:58
 */
@Component
public class JwtTokenEnhancer implements TokenEnhancer {

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
        Map<String, Object> objectObjectHashMap = new HashMap<>();
        objectObjectHashMap.put("enhance", "enhance info");
        objectObjectHashMap.put("ceshi", "测试一下增强令牌!");
        ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(objectObjectHashMap);
        return oAuth2AccessToken;
    }
}

6,配置pojo类

/**
 * @author xzq
 * @description: TODO
 * @date 2022/12/5 13:37
 */
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 null;
    }

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

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

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

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

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

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

配置一个用户的账号密码

/**
 * @author xzq
 * @description: TODO
 * @date 2022/12/5 13:34
 */
@Service
public class UserService 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"));
    }
}

二、创建服务client:SSOA、SSOB 并进行测试

创建client:SSOA
目录如下:
在这里插入图片描述
1,配置yml文件

server:
  port: 8081
  servlet:
    session:
      cookie:
        name: OAUTH2-CLIENT-SESSION01  # 防止cookie冲突

security:
  oauth2:
    client:
      client-id: super # appid
      client-secret: xxx #appsecret
      user-authorization-uri: http://localhost:8080/oauth/authorize #oauth认证地址
      access-token-uri: http://localhost:8080/oauth/token # 获取access_token

    resource:
     jwt:
        key-uri:  http://localhost:8080/oauth/token_key  # 获取和校验JWT(包装获取access_token)

写一个controller

/**
 * @Author xiaozq
 * @Date 2022/12/12 9:26
 * <p>@Description:</p>
 */
@RequestMapping
@RestController
public class SystemAController {

    @GetMapping("/user")
    public Object userinfo(Authentication authentication){
        return authentication;
    }

    @GetMapping("/info")
    public Object ssoinfo(Authentication authentication){
        return  "系统A单点登录";
    }
}

创建client:SSOB: 复制A系统,改一下yml文件的端口号和cookie名设置,如下:

server:
  port: 8082
  servlet:
    session:
      cookie:
        name: OAUTH2-CLIENT-SESSION02  # 防止cookie冲突

security:
  oauth2:
    client:
      client-id: super # appid
      client-secret: xxx #appsecret
      user-authorization-uri: http://localhost:8080/oauth/authorize #oauth认证地址
      access-token-uri: http://localhost:8080/oauth/token # 获取access_token

    resource:
     jwt:
        key-uri:  http://localhost:8080/oauth/token_key  # 获取和校验JWT(包装获取access_token)
        

开测!!!!
1,启动授权服务器,启动服务器A、B注意授权服务器先启动!!!

2,访问服务器A接口: http://localhost:8081/user

在这里插入图片描述
自动跳转到授权服务器的登录页面
输入用户名,密码: admin 123456
在这里插入图片描述
点击登录,则重定向回服务A,成功访问接口
在这里插入图片描述

上述截图响应的内容josn格式化如下:

{
    "authorities": [{
            "authority": "admin"
        }
    ],
    "details": {
        "remoteAddress": "0:0:0:0:0:0:0:1",
        "sessionId": "9143741264A37F6E57C921C0ACCAC86E",
        "tokenValue": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjZXNoaSI6Iua1i-ivleS4gOS4i-WinuW8uuS7pOeJjCEiLCJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbInVzZXIiLCJvcmRlciIsInBheW1lbnQiXSwiZXhwIjoxNjcwODU1MDM2LCJhdXRob3JpdGllcyI6WyJhZG1pbiJdLCJqdGkiOiI1NDc3ZDMzNS01MzMwLTQ4NTQtYWFiMC0xYmU4OTMzZTQxNmEiLCJjbGllbnRfaWQiOiJzdXBlciIsImVuaGFuY2UiOiJlbmhhbmNlIGluZm8ifQ.N2MXqtGFjMkCo5ZaU5TCZdLr1IqfWB3kDmEUO-JH4cA",
        "tokenType": "bearer",
        "decodedDetails": null
    },
    "authenticated": true,
    "userAuthentication": {
        "authorities": [{
                "authority": "admin"
            }
        ],
        "details": null,
        "authenticated": true,
        "principal": "admin",
        "credentials": "N/A",
        "name": "admin"
    },
    "clientOnly": false,
    "oauth2Request": {
        "clientId": "super",
        "scope": ["user", "order", "payment"],
        "requestParameters": {
            "client_id": "super"
        },
        "resourceIds": [],
        "authorities": [],
        "approved": true,
        "refresh": false,
        "redirectUri": null,
        "responseTypes": [],
        "extensions": {},
        "grantType": null,
        "refreshTokenRequest": null
    },
    "credentials": "",
    "principal": "admin",
    "name": "admin"
}

此时在打开一个窗口,访问服务B
在这里插入图片描述
自动省略了上面sign in 登录步骤 ,自动重定向到服务B ,成功访问接口
在这里插入图片描述

至此,单点登录应用完成!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值