以前已經介紹過security的相關的介紹,如今所須要作的就是security和oauth2.0的整合,在原有的基礎上咱們加上一些相關的代碼;代碼實現以下:php
pom.xml:java
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>urity.demo</groupId>
<artifactId>security-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<!--如下兩項須要若是不配置,解析themleaft 會有問題-->
<thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.0.5</thymeleaf-layout-dialect.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Brussels-SR9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis與mysql-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--druid依賴-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.25</version>
</dependency>
<!--redis依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--jasypt加解密-->
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>1.14</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<!--oauth2.0-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!--session集羣管理-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!--zipkin-->
<!-- <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-sleuth</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin</artifactId> </dependency>-->
<!--eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--添加static和templates的依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--config-->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-config</artifactId>-->
<!--</dependency>-->
<!--<dependency>-->
<!--<groupId>org.springframework.cloud</groupId>-->
<!--<artifactId>spring-cloud-starter-bus-amqp</artifactId>-->
<!--</dependency>-->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
複製代碼
這裏咱們須要注意導入依賴的版本,版本太高可能會存在一些未知的問題:mysql
AuthorizationServerConfiguration核心類:git
package urity.demo.oauth2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
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.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import urity.demo.service.RedisAuthenticationCodeServices;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Value("${resource.id:spring-boot-application}")
private String resourceId;
@Value("${access_token.validity_period:36000}")
private int accessTokenValiditySeconds = 36000;
//認證管理 很重要 若是security版本高可能會出坑哦
@Resource
private AuthenticationManager authenticationManager;
@Resource
private RedisAuthenticationCodeServices redisAuthenticationCodeServices;
//定義令牌端點上的安全約束。
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("isAnonymous() || hasAuthority('ROLE_TRUSTED_CLIENT')");
oauthServer.checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
//將ClientDetailsServiceConfigurer(從您的回調AuthorizationServerConfigurer)能夠用來在內存或JDBC實現客戶的細節服務來定義的。客戶端的重要屬性是
//clientId:(必填)客戶端ID。
//secret:(可信客戶端須要)客戶機密碼(若是有)。
//scope:客戶受限的範圍。若是範圍未定義或爲空(默認值),客戶端不受範圍限制。
//authorizedGrantTypes:授予客戶端使用受權的類型。默認值爲空。
//authorities授予客戶的受權機構(普通的Spring Security權威機構)。
//客戶端的詳細信息能夠經過直接訪問底層商店(例如,在數據庫表中JdbcClientDetailsService)或經過ClientDetailsManager接口(這兩種實現ClientDetailsService也實現)來更新運行的應用程序。
//注意:JDBC服務的架構未與庫一塊兒打包(由於在實踐中可能須要使用太多變體)
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//默認值InMemoryTokenStore對於單個服務器是徹底正常的(即,在發生故障的狀況下,低流量和熱備份備份服務器)。大多數項目能夠從這裏開始,也能夠在開發模式下運行,以便輕鬆啓動沒有依賴關係的服務器。
//這JdbcTokenStore是同一件事的JDBC版本,它將令牌數據存儲在關係數據庫中。若是您能夠在服務器之間共享數據庫,則可使用JDBC版本,若是隻有一個,則擴展同一服務器的實例,或者若是有多個組件,則受權和資源服務器。要使用JdbcTokenStore你須要「spring-jdbc」的類路徑。
clients.inMemory()
//client Id
.withClient("normal-app")
.authorizedGrantTypes("authorization_code", "implicit")
.authorities("ROLE_CLIENT")
.scopes("read","write")
.resourceIds(resourceId)
.accessTokenValiditySeconds(accessTokenValiditySeconds)
.and()
.withClient("trusted-app")
.authorizedGrantTypes("client_credentials", "password")
.authorities("ROLE_TRUSTED_CLIENT")
.scopes("read", "write")
.resourceIds(resourceId)
.accessTokenValiditySeconds(accessTokenValiditySeconds)
.secret("secret");
}
//AuthorizationEndpoint能夠經過如下方式配置支持的受權類型AuthorizationServerEndpointsConfigurer。默認狀況下,全部受權類型均受支持,除了密碼(有關如何切換它的詳細信息,請參見下文)。如下屬性會影響受權類型:
//authenticationManager:經過注入密碼受權被打開AuthenticationManager。
//userDetailsService:若是您注入UserDetailsService或者全局配置(例如a GlobalAuthenticationManagerConfigurer),則刷新令牌受權將包含對用戶詳細信息的檢查,以確保該賬戶仍然活動
//authorizationCodeServices:定義AuthorizationCodeServices受權代碼受權的受權代碼服務(實例)。
//implicitGrantService:在批准期間管理狀態。
//tokenGranter:(TokenGranter徹底控制授予和忽略上述其餘屬性)
//在XML授予類型中包含做爲子元素authorization-server。
/** * /oauth/authorize您能夠從該請求中獲取全部數據, * 而後根據須要進行渲染, * 而後全部用戶須要執行的操做都是回覆有關批准或拒絕受權的信息。 * 請求參數直接傳遞給您UserApprovalHandler, * AuthorizationEndpoint因此您能夠隨便解釋數據 * * @param endpoints * @throws Exception */
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(this.authenticationManager);
endpoints.accessTokenConverter(accessTokenConverter());//jwt
endpoints.tokenStore(tokenStore());
//受權碼存儲
endpoints.authorizationCodeServices(redisAuthenticationCodeServices);
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter() {
/** * 重寫加強token的方法 * 自定義返回相應的信息 * */
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String userName = authentication.getUserAuthentication().getName();
// 與登陸時候放進去的UserDetail實現類一直查看link{SecurityConfiguration}
User user = (User) authentication.getUserAuthentication().getPrincipal();
/** 自定義一些token屬性 ***/
final Map<String, Object> additionalInformation = new HashMap<>();
additionalInformation.put("userName", userName);
additionalInformation.put("roles", user.getAuthorities());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
OAuth2AccessToken enhancedToken = super.enhance(accessToken, authentication);
return enhancedToken;
}
};
// 測試用,資源服務使用相同的字符達到一個對稱加密的效果,生產時候使用RSA非對稱加密方式
accessTokenConverter.setSigningKey("123");
return accessTokenConverter;
}
@Bean
public TokenStore tokenStore() {
TokenStore tokenStore = new JwtTokenStore(accessTokenConverter());
return tokenStore;
}
}
複製代碼
RedisAuthenticationCodeServices:github
咱們把受權碼存在了redis中:web
package urity.demo.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.code.RandomValueAuthorizationCodeServices;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.security.oauth2.common.util.SerializationUtils;
//自定義爲使用redis存儲受權碼
@Service
@Slf4j
public class RedisAuthenticationCodeServices extends RandomValueAuthorizationCodeServices {
private static final String AUTH_CODE_KEY = "my_code";
private RedisConnectionFactory connectionFactory;
public RedisAuthenticationCodeServices(RedisConnectionFactory connectionFactory) {
Assert.notNull(connectionFactory, "RedisConnectionFactory required");
this.connectionFactory = connectionFactory;
}
private RedisConnection getConnection() {
return connectionFactory.getConnection();
}
//redis存儲
@Override
protected void store(String code, OAuth2Authentication authentication) {
RedisConnection conn = getConnection();
try {
conn.hSet(AUTH_CODE_KEY.getBytes("utf-8"), code.getBytes("utf-8"),
SerializationUtils.serialize(authentication)
);
} catch (Exception e) {
conn.close();
}
}
@Override
protected OAuth2Authentication remove(String code) {
RedisConnection conn = getConnection();
try {
OAuth2Authentication authentication = null;
try {
authentication = SerializationUtils
.deserialize(conn.hGet(AUTH_CODE_KEY.getBytes("utf-8"),
code.getBytes("utf-8")));
} catch (Exception e) {
e.printStackTrace();
}
if (authentication != null) {
conn.hDel(AUTH_CODE_KEY.getBytes("utf-8"),
code.getBytes("utf-8"));
}
return authentication;
} catch (Exception e) {
e.printStackTrace();
} finally {
conn.close();
}
return null;
}
}
複製代碼
ResourceController:redis
package urity.demo.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/*** * 受保護的資源服務 * @author leftso * */
@RestController
@RequestMapping("/resources")
public class ResourceController {
/** * 須要用戶角色權限 * @return */
@PreAuthorize("hasRole('ROLE_USER')")
@RequestMapping(value="user", method=RequestMethod.GET)
public String helloUser() {
return "hello user";
}
/** * 須要管理角色權限 * * @return */
@PreAuthorize("hasRole('ROLE_ADMIN')")
@RequestMapping(value="admin", method=RequestMethod.GET)
public String helloAdmin() {
return "hello admin";
}
/** * 須要客戶端權限 * * @return */
@PreAuthorize("hasRole('ROLE_CLIENT')")
@RequestMapping(value="client", method=RequestMethod.GET)
public String helloClient() {
return "hello user authenticated by normal client";
}
/** * 須要受信任的客戶端權限 * * @return */
@PreAuthorize("hasRole('ROLE_TRUSTED_CLIENT')")
@RequestMapping(value="trusted_client", method=RequestMethod.GET)
public String helloTrustedClient() {
return "hello user authenticated by trusted client";
}
@RequestMapping(value="principal", method=RequestMethod.GET)
public Object getPrincipal() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return principal;
}
@RequestMapping(value="roles", method=RequestMethod.GET)
public Object getRoles() {
return SecurityContextHolder.getContext().getAuthentication().getAuthorities();
}
}
複製代碼
application.xml:spring
server:
port: 8787
spring:
redis:
host: 127.0.0.1
port: 6379
# password: redis
database: 0
datasource:
url: jdbc:mysql://localhost:3306/test
username: ***
password: ***
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 30
maxWait: 10000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMills: 300000
session:
store-type: none
other:
security:
oauth2:
signKey: oauth
複製代碼