赞
踩
首先这是一种授权协议,个人理解这个协议最大的好处是可以使用第三方client进行登录,比如我们在登录csdn博客的时候,可以不必使用用户名密码,直接微信或者qq扫码登录即可。相比与普通的用户名密码登录授权,OAuth2.0多了一个client的概念,client即是你开发的网站,比如要接入微信的登录,你需要在微信网站应用开发创建审核相应的材料,然后获取APPId,此APPID即是client id,通过此client id,即可以使用微信的授权服务,获取token,进行网站的访问。
此处完全是我自己对于OAuth2的理解,如果不对之处,欢迎大佬批评指正。
1. 授权码许可类型(Authorization Code)
2.隐式许可类型(Implicit)
3.客户端凭据许可(Client Credentials)
4.资源拥有者凭据许可(Resource Owner Password Credentials)
在这里我只说一下最复杂的授权码许可类型,其他的三种请参考OAuth 2.0 的四种方式。
授权码许可类型是最复杂的授权模式,其流程如下所示。登录客户端需要先获得授权码(authorization_code),然后利用code换取access_token。
代码讲解分为两个部分,一个是授权服务,一个是资源服务,资源服务代码主要为了验证生成的access_token能访问相应的资源。
整个项目的pom文件如下。
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns="http://maven.apache.org/POM/4.0.0"
- 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>
-
- <groupId>com.hz.oauth</groupId>
- <artifactId>authority-management-sys-2.0</artifactId>
- <version>1.0</version>
-
- <parent>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-parent</artifactId>
- <version>2.2.1.RELEASE</version>
- </parent>
-
- <modules>
- <module>oauth2-resource</module>
- <module>oauth2-server</module>
- </modules>
-
- <packaging>pom</packaging>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
- <java.version>1.8</java.version>
- <org.apache.common.lang3.version>3.12.0</org.apache.common.lang3.version>
- <spring-framework.cloud.version>Greenwich.SR4</spring-framework.cloud.version>
- <org.apache.common.beanutils.version>1.9.4</org.apache.common.beanutils.version>
- <com.baomidou.mybatis-plus.version>3.4.3</com.baomidou.mybatis-plus.version>
- <mysql.connector.java.version>8.0.12</mysql.connector.java.version>
- <com.alibaba.fastjson.version>1.2.76</com.alibaba.fastjson.version>
-
- <!--Lombok-->
- <lombok.version>1.18.10</lombok.version>
- <commons-io.version>2.6</commons-io.version>
- <javadoc.version>3.0.0</javadoc.version>
- <maven-release-plugin.version>2.5.3</maven-release-plugin.version>
- </properties>
-
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-dependencies</artifactId>
- <version>${spring-framework.cloud.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>${lombok.version}</version>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- <version>${org.apache.common.lang3.version}</version>
- </dependency>
-
- <dependency>
- <groupId>commons-beanutils</groupId>
- <artifactId>commons-beanutils</artifactId>
- <version>${org.apache.common.beanutils.version}</version>
- </dependency>
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- <version>${com.baomidou.mybatis-plus.version}</version>
- </dependency>
-
- <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- <version>${mysql.connector.java.version}</version>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- <version>${com.alibaba.fastjson.version}</version>
- </dependency>
-
- </dependencies>
-
- </dependencyManagement>
-
-
- <build>
- <plugins>
- <!-- for create custom archetype template -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-archetype-plugin</artifactId>
- <version>3.0.1</version>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-release-plugin</artifactId>
- <version>${maven-release-plugin.version}</version>
- </plugin>
- </plugins>
-
-
- </build>
-
- </project>
最为重要的是两个配置文件类,一个是授权服务的配置,一个是security的安全服务配置,security的安全服务配置重要的是把授权服务相应的地址配置允许匿名访问。
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns="http://maven.apache.org/POM/4.0.0"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
- <parent>
- <groupId>com.hz.oauth</groupId>
- <artifactId>authority-management-sys-2.0</artifactId>
- <version>1.0</version>
- </parent>
-
- <artifactId>oauth2-server</artifactId>
- <modelVersion>4.0.0</modelVersion>
-
- <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.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- </dependency>
- <dependency>
- <groupId>org.apache.commons</groupId>
- <artifactId>commons-lang3</artifactId>
- </dependency>
- <dependency>
- <groupId>commons-beanutils</groupId>
- <artifactId>commons-beanutils</artifactId>
- </dependency>
- <dependency>
- <groupId>mysql</groupId>
- <artifactId>mysql-connector-java</artifactId>
- </dependency>
- <dependency>
- <groupId>com.baomidou</groupId>
- <artifactId>mybatis-plus-boot-starter</artifactId>
- </dependency>
- <dependency>
- <groupId>com.alibaba</groupId>
- <artifactId>fastjson</artifactId>
- </dependency>
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-thymeleaf</artifactId>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <!-- for create custom archetype template -->
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-archetype-plugin</artifactId>
- <version>3.0.1</version>
- </plugin>
-
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-release-plugin</artifactId>
- <version>${maven-release-plugin.version}</version>
- </plugin>
- <plugin>
- <groupId>org.sonarsource.scanner.maven</groupId>
- <artifactId>sonar-maven-plugin</artifactId>
- <version>3.0.2</version>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-compiler-plugin</artifactId>
- <configuration>
- <source>1.8</source>
- <target>1.8</target>
- </configuration>
- </plugin>
- </plugins>
- <resources>
- <resource>
- <directory>src/main/java</directory>
- <includes>
- <include>**/*.xml</include>
- </includes>
- </resource>
- <resource>
- <directory>src/main/webapp</directory>
- <targetPath>META-INF/resources</targetPath>
- <includes>
- <include>**/**</include>
- </includes>
- </resource>
- <resource>
- <directory>src/main/resources</directory>
- <includes>
- <include>**/**</include>
- </includes>
- </resource>
- <resource>
- <directory>src/main/resources</directory>
- <filtering>true</filtering>
- <excludes>
- <exclude>**/*.jks</exclude>
- </excludes>
- </resource>
- <resource>
- <directory>src/main/resources</directory>
- <filtering>false</filtering>
- <includes>
- <include>**/*.jks</include>
- </includes>
- </resource>
- </resources>
-
-
- </build>
-
- </project>
在该类配置中,授权码的保存,用户的授权记录保存在数据库中,其生成数据库代码为
- DROP TABLE IF EXISTS `oauth_approvals`;
- CREATE TABLE `oauth_approvals` (
- `userId` varchar(256) DEFAULT NULL,
- `clientId` varchar(256) DEFAULT NULL,
- `partnerKey` varchar(32) DEFAULT NULL,
- `scope` varchar(256) DEFAULT NULL,
- `status` varchar(10) DEFAULT NULL,
- `expiresAt` datetime DEFAULT NULL,
- `lastModifiedAt` datetime DEFAULT NULL
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
-
- DROP TABLE IF EXISTS `oauth_code`;
- CREATE TABLE `oauth_code` (
- `code` varchar(255) DEFAULT NULL,
- `authentication` blob
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
SysAuthorizationConfig类配置代码
- /**
- * @program: OAuth-2.0
- * @author: zgr
- * @create: 2021-07-20 11:42
- **/
- @Configuration
- @EnableAuthorizationServer
- public class SysAuthorizationConfig extends AuthorizationServerConfigurerAdapter {
-
-
- private final DataSource dataSource;
-
- private final SysClientService sysClientService;
-
- private final AuthenticationManager authenticationManager;
-
- public SysAuthorizationConfig(DataSource dataSource, SysClientService sysClientService, AuthenticationManager authenticationManager) {
- this.dataSource = dataSource;
- this.sysClientService = sysClientService;
- this.authenticationManager = authenticationManager;
- }
-
-
- /**
- * 维护一套客户端的信息 即第三方应用软件申请时记录的一些基本信息
- *
- * @param clients
- * @throws Exception
- */
- @Override
- public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
- clients.withClientDetails(sysClientService);
- }
-
- /**
- * 打开验证token的访问权限
- *
- * @param security
- */
- @Override
- public void configure(AuthorizationServerSecurityConfigurer security) {
- security
- .allowFormAuthenticationForClients()
- .tokenKeyAccess("isAuthenticated()")
- .checkTokenAccess("permitAll()")
- .passwordEncoder(new BCryptPasswordEncoder());
- }
-
-
- /**
- * 配置了令牌存储方式为jwt
- * 配置JWT Token的非对称加密来进行签名
- * 配置一个自定义的Token增强器,把更多信息放入Token中
- * 配置使用JDBC数据库方式来保存用户的授权批准记录
- *
- * @param endpoints
- */
- @Override
- public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
- TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
- tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtTokenEnhancer()));
-
- endpoints.approvalStore(approvalStore())
- .authorizationCodeServices(authorizationCodeServices())
- .tokenEnhancer(tokenEnhancerChain)
- .tokenStore(tokenStore())
- .authenticationManager(authenticationManager);
- }
-
- /**
- * 使用数据库方式来保存授权码
- *
- * @return
- */
- @Bean
- public AuthorizationCodeServices authorizationCodeServices() {
- return new JdbcAuthorizationCodeServices(dataSource);
- }
-
- /**
- * 采用jwt方式存储token
- *
- * @return
- */
- @Bean
- public TokenStore tokenStore() {
- return new JwtTokenStore(jwtTokenEnhancer());
- }
-
- /**
- * 使用数据库方式存储用户的批准授权记录
- *
- * @return
- */
- @Bean
- public JdbcApprovalStore approvalStore() {
- return new JdbcApprovalStore(dataSource);
- }
-
- /**
- * 自定义token增强器
- *
- * @return
- */
- @Bean
- public TokenEnhancer tokenEnhancer() {
- return new SysTokenEnhancer();
- }
-
- /**
- * jwt 验证
- *
- * @return
- */
- @Bean
- protected JwtAccessTokenConverter jwtTokenEnhancer() {
- JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
- // 设置jwt加解密秘钥,不设置会随机一个
- jwtAccessTokenConverter.setSigningKey(Constant.JWT_SECRET);
- return jwtAccessTokenConverter;
- }
- }
- **
- * @program: authority-management-sys-2.0
- * @author: zgr
- * @create: 2021-08-03 09:55
- **/
-
- @Configuration
- @EnableWebSecurity
- public class SysSecurityConfig extends WebSecurityConfigurerAdapter {
-
-
- private final SysUserService sysUserService;
- private final SysLogoutHandler sysLogoutHandler;
- private final SysLogoutSuccessHandler sysLogoutSuccessHandler;
-
-
- public SysSecurityConfig(SysUserService sysUserService, SysLogoutHandler sysLogoutHandler, SysLogoutSuccessHandler sysLogoutSuccessHandler) {
- this.sysUserService = sysUserService;
- this.sysLogoutHandler = sysLogoutHandler;
- this.sysLogoutSuccessHandler = sysLogoutSuccessHandler;
- }
-
- /**
- * 配置认证管理器
- *
- * @return
- * @throws Exception
- */
- @Bean
- @Override
- public AuthenticationManager authenticationManagerBean() throws Exception {
- return super.authenticationManagerBean();
- }
-
-
- /**
- * 这里是对认证管理器的添加配置,自定义用户详情
- *
- * @param auth
- * @throws Exception
- */
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(sysUserService).passwordEncoder(new BCryptPasswordEncoder())
- ;
- }
-
- //web ignore比较适合配置前端相关的静态资源,它是完全绕过spring security的所有filter的
- @Override
- public void configure(WebSecurity web) throws Exception {
- web.ignoring().antMatchers("/resource");
- }
-
-
- /**
- * 安全请求配置,这里配置的是security的部分,这里配置全部通过,安全拦截在资源服务的配置文件中配置,
- * 要不然访问未验证的接口将重定向到登录页面,前后端分离的情况下这样并不友好,无权访问接口返回相关错误信息即可
- *
- * @param http
- * @return void
- */
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- .formLogin().loginPage("/login").and()
- .logout().addLogoutHandler(sysLogoutHandler).logoutSuccessHandler(sysLogoutSuccessHandler)
- .and()
- // 由于使用的是JWT,我们这里不需要csrf
- .csrf().disable().cors().and()
- .authorizeRequests()
- .antMatchers("/login", "/oauth/authorize").permitAll()
- // 除上面外的所有请求全部需要鉴权认证
- .anyRequest().authenticated();
-
- }
- }
在SysAuthorizationConfig中,我们配置的是客户端 服务为自定义的sysClientService。实际应用中,通过client id查出对应客户端的信息,查询其拥有的resource资源权限,生成access_token。
- /**
- * 维护一套客户端的信息 即第三方应用软件申请时记录的一些基本信息
- *
- * @param clients
- * @throws Exception
- */
- @Override
- public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
- clients.withClientDetails(sysClientService);
- }
- @Data
- @Builder
- @AllArgsConstructor
- @NoArgsConstructor
- public class SysClientDetails implements ClientDetails {
-
- private String clientId;
-
- private Set<String> resourceId;
-
- private Boolean isSecretRequired;
-
- private String clientSecret;
-
- private Boolean isScoped;
-
- private Set<String> scope;
-
- private Set<String> authorizedGrantTypes;
-
- private Set<String> registeredRedirectUri;
-
- private Integer accessTokenValiditySeconds;
-
- private Integer refreshTokenValiditySeconds;
-
- private Boolean isAutoApprove;
-
- private Map<String, Object> additionalInformation;
-
- @Override
- public String getClientId() {
- return this.clientId;
- }
-
- @Override
- public Set<String> getResourceIds() {
- return this.resourceId;
- }
-
- @Override
- public boolean isSecretRequired() {
- return this.isSecretRequired;
- }
-
- @Override
- public String getClientSecret() {
- return this.clientSecret;
- }
-
- @Override
- public boolean isScoped() {
- return this.isScoped;
- }
-
- @Override
- public Set<String> getScope() {
- return this.scope;
- }
-
- @Override
- public Set<String> getAuthorizedGrantTypes() {
- return this.authorizedGrantTypes;
- }
-
- @Override
- public Set<String> getRegisteredRedirectUri() {
- return this.registeredRedirectUri;
- }
-
- @Override
- public Collection<GrantedAuthority> getAuthorities() {
- return Collections.emptyList();
- }
-
- @Override
- public Integer getAccessTokenValiditySeconds() {
- return this.accessTokenValiditySeconds;
- }
-
- @Override
- public Integer getRefreshTokenValiditySeconds() {
- return this.refreshTokenValiditySeconds;
- }
-
- @Override
- public boolean isAutoApprove(String scope) {
- return this.isAutoApprove;
- }
-
- @Override
- public Map<String, Object> getAdditionalInformation() {
- return this.additionalInformation;
- }
- }
-
-
- /**
- * @program: authority-management-sys-2.0
- * @author: zgr
- * @create: 2021-08-03 10:12
- **/
-
- @Service
- @Slf4j
- public class SysClientServiceImpl implements SysClientService {
-
-
- @Override
- public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
- log.info("查找客户端id为 {} 的客户端详情", clientId);
-
- //实际使用中可以从数据库查询客户端详细信息,本例子中直接构造一个客户端
-
- Set<String> resourceIds = new HashSet<>();
- resourceIds.add("userservice1");
-
- Set<String> scopes = new HashSet<>(1);
- scopes.add("sever");
-
- Set<String> grantTypes = new HashSet<>(5);
- grantTypes.add("password");
- grantTypes.add("authorization_code");
-
- Set<String> registeredRedirectUris = new HashSet<>(1);
- registeredRedirectUris.add("https://baidu.com");
-
- return SysClientDetails.builder()
- //客户端id
- .clientId("userservice1")
- //拥有的资源id
- .resourceId(resourceIds)
- //是否需要secret
- .isSecretRequired(true)
- //secret加密方式
- .clientSecret(new BCryptPasswordEncoder().encode("1234"))
- //权限范围
- .scope(scopes)
- //支持的授权模式,四种模式支持哪几种
- .authorizedGrantTypes(grantTypes)
- //官方注册的回调地址,这个地址是需要和授权请求的回调地址一致
- .registeredRedirectUri(registeredRedirectUris)
- .isScoped(true)
- //access_token的有效时间
- .accessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(2))
- //refresh_token的有效时间
- .refreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30))
- //是否可以自主授权
- .isAutoApprove(true)
- .build();
- }
- }
1.资源有着凭据许可
完整的返回数据:
- {
- "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiZXhwIjoxNjI4NzU3NTMzLCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiODAwZjgwMmUtZTUzNS00N2RiLTkzOTUtZjY5ZmQxM2Q3ZjNjIiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.N8HLGnDbRiN560SuE_a4IlqEZIk3yCpzpyEXH3VDb1w",
- "token_type": "bearer",
- "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiYXRpIjoiODAwZjgwMmUtZTUzNS00N2RiLTkzOTUtZjY5ZmQxM2Q3ZjNjIiwiZXhwIjoxNjMxMTc2NzMyLCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiZDMyNGQ4ZjEtMjBiMy00MTg2LTk4NzMtZjg2NDhkNjI1ZTc5IiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.KbqODRoK0hjrrHWeKvtZ2HNkcREPFmuv7yvOrZGzEnE",
- "expires_in": 172799,
- "scope": "sever",
- "userDetails": {
- "id": 1,
- "username": "admin",
- "password": null,
- "roles": [
- {
- "id": 1,
- "name": "READ"
- },
- {
- "id": 1,
- "name": "WRITE"
- }
- ],
- "authorities": [
- {
- "authority": "READ"
- },
- {
- "authority": "WRITE"
- }
- ],
- "enabled": true,
- "credentialsNonExpired": true,
- "accountNonExpired": true,
- "accountNonLocked": true
- },
- "jti": "800f802e-e535-47db-9395-f69fd13d7f3c"
- }
2.授权码模式
在浏览器输入地址http://localhost:8086/api/v1/oauth/authorize?response_type=code&client_id=userservice1&redirect_uri=https://baidu.com,会跳转到授权页面,授权页面利用spring mvc定义了一个页面放在resources目录下。
- <!DOCTYPE html>
- <html class="uk-height-1-1" xmlns:th="http://www.thymeleaf.org">
- <head>
- <meta charset="UTF-8"/>
- <title>OAuth2 Demo</title>
- <link href="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.26.3/css/uikit.gradient.min.css" rel="stylesheet"/>
- </head>
-
- <body class="uk-height-1-1">
-
- <div class="uk-vertical-align uk-text-center uk-height-1-1">
- <div class="uk-vertical-align-middle" style="width: 250px;">
- <h1>Login Form</h1>
-
- <p class="uk-text-danger" th:if="${param.error}">
- 用户名或密码错误...
- </p>
-
- <form class="uk-panel uk-panel-box uk-form" method="post" th:action="@{/login}">
- <div class="uk-form-row">
- <input class="uk-width-1-1 uk-form-large" name="username" placeholder="Username" type="text"
- value="admin"/>
- </div>
- <div class="uk-form-row">
- <input class="uk-width-1-1 uk-form-large" name="password" placeholder="Password" type="password"
- value="admin"/>
- </div>
- <div class="uk-form-row">
- <button class="uk-width-1-1 uk-button uk-button-primary uk-button-large">Login</button>
- </div>
- </form>
-
- </div>
- </div>
- </body>
- </html>
跳转到登录页面,点击登录,登录成功后,跳转到百度页面,并得到code授权码
拿到获取的code,生成access_token
完整的返回信息
- {
- "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiZXhwIjoxNjI4NzU4Mjc2LCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiZGM5Y2FjN2UtN2M3My00YThkLWEzMzYtZTI2ZmNjYzllODE0IiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.sOhH-eFcldWnnOfZmDqyuPkl4Qkg8LyJnPZ0gwubBWc",
- "token_type": "bearer",
- "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiYXRpIjoiZGM5Y2FjN2UtN2M3My00YThkLWEzMzYtZTI2ZmNjYzllODE0IiwiZXhwIjoxNjMxMTc3NDc2LCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiNDdmYjUzMDctYzcyYy00ZjI1LWI4MjEtNTY2MGNlYWYyZGMwIiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.vmtnmLWsaGBxFxAZ-0nsETBUsZ3muSFKqjffAweRZTQ",
- "expires_in": 172799,
- "scope": "sever",
- "userDetails": {
- "id": 1,
- "username": "admin",
- "password": null,
- "roles": [
- {
- "id": 1,
- "name": "READ"
- },
- {
- "id": 1,
- "name": "WRITE"
- }
- ],
- "authorities": [
- {
- "authority": "READ"
- },
- {
- "authority": "WRITE"
- }
- ],
- "enabled": true,
- "credentialsNonExpired": true,
- "accountNonExpired": true,
- "accountNonLocked": true
- },
- "jti": "dc9cac7e-7c73-4a8d-a336-e26fccc9e814"
- }
oauth2-resource为资源,用户在获得access_token后,携带access_token访问资源。最重要的是资源配置类。
- @Configuration
- //启用资源服务器
- @EnableResourceServer
- //启用方法注解方式来进行权限控制
- @EnableGlobalMethodSecurity(prePostEnabled = true)
- public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
-
-
- /**
- * 声明了资源服务器的ID是userservice,声明了资源服务器的TokenStore是JWT
- * 注意,这里硬编码了资源id,这个资源id和client所拥有的的资源id需要匹配
- *
- * @param resources
- * @throws Exception
- */
- @Override
- public void configure(ResourceServerSecurityConfigurer resources) {
- resources.resourceId("userservice1").tokenStore(tokenStore());
- }
-
- /**
- * 配置TokenStore
- *
- * @return
- */
- @Bean
- public TokenStore tokenStore() {
- return new JwtTokenStore(jwtAccessTokenConverter());
- }
-
- /**
- * 配置解析
- *
- * @return
- */
- @Bean
- protected JwtAccessTokenConverter jwtAccessTokenConverter() {
- JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
- //和授权服务的key相同
- converter.setSigningKey(Constant.JWT_SECRET);
- return converter;
- }
- }
security配置接口访问权限
- @Configuration
- @EnableWebSecurity
- public class ResourceSecurityConfig extends WebSecurityConfigurerAdapter {
-
- //web ignore比较适合配置前端相关的静态资源,它是完全绕过spring security的所有filter的
- @Override
- public void configure(WebSecurity web) throws Exception {
- web.ignoring().antMatchers("/resource");
- }
-
-
- /**
- * 安全请求配置,这里配置的是security的部分,这里配置全部通过,安全拦截在资源服务的配置文件中配置,
- * 要不然访问未验证的接口将重定向到登录页面,前后端分离的情况下这样并不友好,无权访问接口返回相关错误信息即可
- *
- * @param http
- * @return void
- */
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http.authorizeRequests()
- .antMatchers("/user/**").authenticated()
- .anyRequest().permitAll();
-
- }
- }
ctrl接口测试类
- **
- * @author honorzhang
- */
-
- @RestController
- @RequestMapping("/user")
- public class ResourceUserController {
-
- @Autowired
- private TokenStore tokenStore;
-
- /***
- * 读权限或写权限可访问,返回登录用户名
- * @param authentication
- * @return
- */
- @PreAuthorize("hasAuthority('READ') or hasAuthority('WRITE')")
- @GetMapping("/name")
- public BaseResponse<String> name(OAuth2Authentication authentication) {
- return BaseResponse.success(authentication.getName());
- }
-
- /**
- * 读权限或写权限可访问,返回登录用户信息
- *
- * @param authentication
- * @return
- */
- @PreAuthorize("hasAuthority('READ') or hasAuthority('WRITE')")
- @GetMapping
- public BaseResponse<OAuth2Authentication> read(OAuth2Authentication authentication) {
- return BaseResponse.success(authentication);
- }
-
- /**
- * 只有写权限可以访问,返回访问令牌中的额外信息
- *
- * @param authentication
- * @return
- */
- @PreAuthorize("hasAuthority('WRITE') and hasAuthority('ADMIN')")
- @PostMapping
- public BaseResponse<Object> write(OAuth2Authentication authentication) {
- OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
- OAuth2AccessToken accessToken = tokenStore.readAccessToken(details.getTokenValue());
- return BaseResponse.success(accessToken.getAdditionalInformation().getOrDefault("userDetails", null));
- }
- }
access_token 权限包含WRITE 和 READ权限,没有ADMIN权限。
访问read接口,权限通过。
访问write接口,没有ADMIN权限,不能访问
本文是根据极客时间的课程,利用spring cloud oauth2 实现的OAuth2.0权限控制。有很多是基于自己的理解,如有不正确之处,请互相指教。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。