oauth2.0系统学习-spring sercurity+oauth2.0+JWT 单点登陆设计
作者:互联网
oauth2.0系统学习-spring sercurity+oauth2.0+JWT 单点登陆设计
一、spring sercurity+oauth2.0+JWT 单点登陆设计
- 认证服务器和资源服务器在同一台机器(网关)
- 密码授权模式
- 去掉客户端信息表
- 去掉用户授权表
pom.xml配置
<?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 https://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>2.5.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>oauth-learn2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>oauth-learn2</name>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--security end-->
<!--数据库依赖 begin-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--数据库依赖 end-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.application.name=oauth-authorizationServer
server.port=8083
spring.datasource.url = jdbc:mysql://localhost:3306/testoauth?useUnicode=true
spring.datasource.username=root
spring.datasource.password=ylx123
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.main.allow-bean-definition-overriding=true
UserBean
package com.example.oauthlearn2.bean;
import lombok.Data;
/**
* @Description 请描述下该类是做什么的
* @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
* @Date 2021/9/25 11:13
* @Verson 1.0
**/
@Data
public class UserBean {
private Long id;
private String userName;
private String password;
}
AuthorizationServer
package com.example.oauthlearn2.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
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.Arrays;
/**
* @Description 请描述下该类是做什么的
* @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
* @Date 2021/9/25 11:14
* @Verson 1.0
**/
@Configuration
@EnableAuthorizationServer // 配置授权服务。
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
@Autowired
// 授权码服务
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
// 认证管理
private AuthenticationManager authenticationManager;
@Autowired
// 令牌管理服务
private AuthorizationServerTokenServices authorizationServerTokenServices;
@Autowired
// 令牌存储策略
private TokenStore tokenStore;
@Autowired
// 客户端详情服务,也就是configure(ClientDetailsServiceConfigurer clients)方法
private ClientDetailsService clientDetailsService;
@Autowired
// 加密方式
private PasswordEncoder passwordEncoder;
// 用来配置客户端详情服务
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 这里是第三方合作用户的客户id,秘钥的配置
// 使用in-memory存储
clients.inMemory()
// client_id,用户账号
.withClient(OauthCanstant.clientId)
// 客户端密钥
.secret(new BCryptPasswordEncoder().encode(OauthCanstant.secret))
// 资源列表,资源标识
.resourceIds(OauthCanstant.resourceIds)
// 授权类型(4种)
.authorizedGrantTypes(OauthCanstant.authorizedGrantTypes)
// 客戶端允许的授权范围
.scopes(OauthCanstant.scopes)
// false跳转到授权页面,让用户点击授权,如果是true,相当于自动点击授权,就不跳转授权页面
.autoApprove(OauthCanstant.autoApprove)//
// 加上验证回调地址,返回授权码信息
.redirectUris(OauthCanstant.redirectUris);
}
// 令牌管理服务
@Bean
public AuthorizationServerTokenServices authorizationServerTokenServices() {
DefaultTokenServices service = new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);
service.setSupportRefreshToken(true);// 支持刷新
service.setTokenStore(tokenStore);// 令牌存储
// 令牌增强
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
service.setTokenEnhancer(tokenEnhancerChain);
service.setAccessTokenValiditySeconds(OauthCanstant.accessTokenValiditySeconds); // 令牌默认有效期2小时
service.setRefreshTokenValiditySeconds(OauthCanstant.refreshTokenValiditySeconds); // 刷新令牌默认有效期3天
return service;
}
@Override
// 用来配置令牌(token)的访问端点
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
// 指定认证管理器
.authenticationManager(authenticationManager)
// 授权码模式需要
.authorizationCodeServices(authorizationCodeServices)
// 令牌管理服务
.tokenServices(authorizationServerTokenServices)
// jwt格式Token
.accessTokenConverter(accessTokenConverter)
// 允许post提交
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
@Bean
// 授权码服务器
public AuthorizationCodeServices authorizationCodeServices() {
// 授权码模式的授权码采用内存方式存储
return new InMemoryAuthorizationCodeServices();
}
@Override
// 用来配置令牌端点的安全约束,拦截规则
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 提供公有密匙的端点,如果你使用JWT令牌的话, 允许
.tokenKeyAccess(OauthCanstant.permitAll)
// oauth/check_token:用于资源服务访问的令牌解析端点,允许
.checkTokenAccess(OauthCanstant.permitAll)
// 表单认证,申请令牌
.allowFormAuthenticationForClients();
}
}
OauthCanstant
package com.example.oauthlearn2.config;
/**
* @Description 请描述下该类是做什么的
* @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
* @Date 2021/9/25 11:15
* @Verson 1.0
**/
public interface OauthCanstant {
String clientId = "c1";
String secret = "secret";
String resourceIds = "res1";
String authorizedGrantTypes = "password";
String scopes = "all";
boolean autoApprove = false;
String redirectUris = "http://www.baidu.com";
String permitAll = "permitAll()";
// 令牌默认有效期2小时
int accessTokenValiditySeconds = 7200;
// 刷新令牌默认有效期3天
int refreshTokenValiditySeconds = 259200;
// 对称秘钥,资源服务器使用该秘钥来验证
String SIGNING_KEY = "uaa123";
}
ResouceServerConfig
package com.example.oauthlearn2.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
/**
* @Description 请描述下该类是做什么的
* @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
* @Date 2021/9/25 11:15
* @Verson 1.0
**/
@Configuration
@EnableResourceServer
public class ResouceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
// 令牌存储策略
private TokenStore tokenStore;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources
// 资源 id
.resourceId(OauthCanstant.resourceIds)
// // 令牌服务
.tokenStore(tokenStore)
.stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/**")
// 所有的访问,授权访问都要是all,和认证服务器的授权范围一一对应
.access("#oauth2.hasScope('"+OauthCanstant.scopes+"')")
//去掉防跨域攻击
.and().csrf().disable()
//session管理
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
TokenConfig
package com.example.oauthlearn2.config;
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;
/**
* @Description 请描述下该类是做什么的
* @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
* @Date 2021/9/25 11:16
* @Verson 1.0
**/
@Configuration
public class TokenConfig {
@Bean
public TokenStore tokenStore() {
// JWT令牌存储方案
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(OauthCanstant.SIGNING_KEY); // 对称秘钥,资源服务器使用该秘钥来验证
return converter;
}
}
WebSecurityConfig
package com.example.oauthlearn2.config;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @Description 请描述下该类是做什么的
* @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
* @Date 2021/9/25 11:16
* @Verson 1.0
**/
@Configuration
// @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// 认证管理器
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
// 密码编码器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 安全拦截机制
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated()// 所有其他请求必须认证通过
.and().formLogin().loginPage("/login").successForwardUrl("/login-success")// 自定义登录成功的页面地址
.permitAll().and().logout().permitAll().and()// 会话管理
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);// 会话管理
}
}
LoginController
package com.example.oauthlearn2.controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description 请描述下该类是做什么的
* @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
* @Date 2021/9/25 11:17
* @Verson 1.0
**/
@RestController
public class LoginController {
@RequestMapping(value = "/login-success")
public String loginSuccess() {
return getUsername() + " login-success 登录成功";
}
/**
* 测试资源1
*
* @return
*/
@GetMapping(value = "/admin/p1")
// @PreAuthorize("hasAuthority('p1')")//拥有p1权限才可以访问
public String r1() {
return " /admin/p1 " + getUsername() + "访问资源1";
}
/**
* 测试资源2
*
* @return
*/
@GetMapping(value = "/user/p2")
// @PreAuthorize("hasAuthority('p2')")//拥有p2权限才可以访问
public String r2() {
return "/user/p2 " + getUsername() + "访问资源2";
}
// 获取当前用户信息
private String getUsername() {
String username = null;
// 当前认证通过的用户身份
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 用户身份
Object principal = authentication.getPrincipal();
if (principal == null) {
username = "匿名";
}
if (principal instanceof UserDetails) {
UserDetails userDetails = (UserDetails) principal;
username = userDetails.getUsername();
} else {
username = principal.toString();
}
return username;
}
}
UserDao
package com.example.oauthlearn2.dao;
import com.example.oauthlearn2.bean.UserBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @Description 请描述下该类是做什么的
* @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
* @Date 2021/9/25 11:17
* @Verson 1.0
**/
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
// 根据账号查询用户信息
public UserBean getUserByUsername(String username) {
String sql = "select * from user where user_name = ?";
// 连接数据库查询用户
List<UserBean> list = jdbcTemplate.query(sql, new Object[] { username },
new BeanPropertyRowMapper<>(UserBean.class));
if (list != null && list.size() == 1) {
return list.get(0);
}
return null;
}
}
SpringDataUserDetailsService
package com.example.oauthlearn2.service;
import com.example.oauthlearn2.bean.UserBean;
import com.example.oauthlearn2.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* @Description 请描述下该类是做什么的
* @Author <a href="mailto:yuanlx@smartdot.com.cn">袁凌霄</a>
* @Date 2021/9/25 11:18
* @Verson 1.0
**/
@Service
public class SpringDataUserDetailsService implements UserDetailsService {
@Autowired
UserDao userDao;
// 根据账号查询用户信息,
// 通过@Service将SpringDataUserDetailsService注入容器,通过UserDetailsService接口表明该类的类型是UserDetailsService
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 将来连接数据库根据账号查询用户信息
UserBean bean = userDao.getUserByUsername(username);
if (bean == null) {
// 如果用户查不到,返回null,由provider来抛出异常
return null;
}
// 添加p1权限,权限是随便加的,这里已经没有意义了
UserDetails userDetails = User.withUsername(bean.getUserName()).password(bean.getPassword()).authorities("p1")
.build();
return userDetails;
}
}
测试
获取admin的token
http://localhost:8083/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=admin&password=admin
admin请求可以访问的资源:
http://localhost:8083/admin/p1
头部信息中添加 Authorization,值为:“Bearer ” + token
标签:spring,JWT,springframework,annotation,org,import,security,oauth2.0,public 来源: https://blog.csdn.net/ylx814056815/article/details/120470325