SpringSecurity+OAuth2.0+JWT实现单点登录应用
gitee项目练习地址:https://gitee.com/xzq25_com/springsecurity.oauth2
1,导入依赖:
<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>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.9.1version>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
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"));
}
}
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
* @Description:
*/
@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 ,成功访问接口
至此,单点登录应用完成!!!!