springboot使用Spring Security+OAuth2做許可權控制
文章來源:http://lxgandlz.cn/404.html
前面有一篇文章Spring+Spring Security+OAuth2實現REST API許可權控制,講了Spring+Spring Security+OAuth2來實現REST API許可權控制,出於快速實現的原因,裡面的使用者資訊和認證token都是儲存在記憶體中。這樣並不符合實際專案場景。所以,這篇文章就是講述如何從資料庫中載入使用者資訊,並且將認證token儲存在redis中。
原始碼地址:https://github.com/li5454yong/springboot-security-oauth2.git
首先來看專案結構
這個專案中用到了三張表,執行專案會自動在資料庫建立這三張表。
1、pom依賴
XHTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent>
<repositories> <repository> <id>aliyunRepository</id> <name>myRepository</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories>
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.7</java.version> <spring-security-oauth2.version>2.0.3.RELEASE</spring-security-oauth2.version> </properties>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
</dependencies> |
相對於Spring的整合,這裡去除了Spring的依賴,引入了Spring Boot的依賴、Spring data JPA依賴、redis依賴。
2、自定義UserDetailService
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/** * Created by lxg * on 2017/2/20. */ public class MyUserDetailsService implements UserDetailsService {
@Autowired private UserService userService;
@Autowired private UserRoleService userRoleService; /** * 根據使用者名稱獲取登入使用者資訊 * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userService.findByUsername(username); if(user == null){ throw new UsernameNotFoundException("使用者名稱:"+ username + "不存在!"); } Collection<SimpleGrantedAuthority> collection = new HashSet<SimpleGrantedAuthority>(); Iterator<String> iterator = userRoleService.findRoles(user.getId()).iterator(); while (iterator.hasNext()){ collection.add(new SimpleGrantedAuthority(iterator.next())); }
return new org.springframework.security.core.userdetails.User(username,user.getPassword(),collection); } } |
這裡只需要實現UserDetailsService介面,實現loadUserByUsername方法,通過使用者名稱來獲取到使用者的資訊。如果使用者不存在可以丟擲UsernameNotFoundException。然後通過userid來獲取使用者角色。因為一個使用者可能會擁有多個角色,所以這裡返回的是一個List。最後返回的是一個org.springframework.security.core.userdetails.User物件,裡面包含了使用者名稱、密碼、角色。也可以根據自己需要去設定使用者是否被鎖定、是否可用等資訊。
3、Security配置
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
/** * security配置 * * @author lxg * * 2017年2月17日上午11:13:55 */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired private ClientDetailsService clientDetailsService;
@Autowired private RedisConnectionFactory redisConnection;
@Bean public MyUserDetailsService myUserDetailsService(){ return new MyUserDetailsService(); } @Autowired public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(myUserDetailsService()) .passwordEncoder(new Md5PasswordEncoder()); }
@Override protected void configure(HttpSecurity http) throws Exception { http .anonymous().disable() .authorizeRequests() .antMatchers("/oauth/token").permitAll(); }
@Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }
@Bean public TokenStore tokenStore() { return new RedisTokenStore(redisConnection); }
@Bean @Autowired public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){ TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler(); handler.setTokenStore(tokenStore); handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService)); handler.setClientDetailsService(clientDetailsService); return handler; } @Bean @Autowired public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception { TokenApprovalStore store = new TokenApprovalStore(); store.setTokenStore(tokenStore); return store; } } |
只要在這裡面例項化MyUserDetailService,然後通過AuthenticationManagerBuilder的userDetailsService方法,設定進去就OK了。passwordEncoder方法設定的是使用者密碼的加密方式,這裡設定的是MD5加密,所以使用者從前端登入時傳過來的密碼,在使用Security驗證時會自動使用MD5加密。
4、redis配置
TeX
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# Redis資料庫索引(預設為0) spring.redis.database=1 # Redis伺服器地址 spring.redis.host=192.168.0.12 # Redis伺服器連線埠 spring.redis.port=6379 # Redis伺服器連線密碼(預設為空) spring.redis.password= # 連線池最大連線數(使用負值表示沒有限制) spring.redis.pool.max-active=8 # 連線池最大阻塞等待時間(使用負值表示沒有限制) spring.redis.pool.max-wait=-1 # 連線池中的最大空閒連線 spring.redis.pool.max-idle=8 # 連線池中的最小空閒連線 spring.redis.pool.min-idle=0 # 連線超時時間(毫秒) spring.redis.timeout=0 |
Spring Boot中整合Redis非常簡單,只需要引入spring-boot-starter-redis依賴包,然後配置上鍊接資訊就行了,Spring Boot的約束優於配置比起Spring著實方便不少。不知道Spring如何整合Redis的這裡有一個整合的Demo,可以拿去參考一下。https://github.com/li5454yong/spring-redis.git
5、RedisTokenStore配置
Java
1 2 3 4 5 6 7 |
@Autowired private RedisConnectionFactory redisConnection;
@Bean public TokenStore tokenStore() { return new RedisTokenStore(redisConnection); } |
TokenStore預設有四種實現,我們這裡使用的是RedisTokenStore,他的構造方法中需要一個redis連結工廠。我們直接將Spring容器管理的redisConnectionFactory注入進來即可。
6、測試
這裡的測試和Spring整合的測試方法一樣,這裡就不在贅述,不明白的可以參考上一篇文章。
7、踩坑
2017-09-09更新
一些朋友在使用demo時遇到幾個問題,發現有一些地方沒說清楚,這裡更新一下。
1、密碼加密問題
Java
1 2 3 4 5 6 |
@Autowired public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception { auth .userDetailsService(myUserDetailsService()) .passwordEncoder(new Md5PasswordEncoder()); } |
程式碼中已經配置了密碼使用MD5加密,所以使用demo時,插入到資料庫的密碼要使用MD5加密一樣。
如果你想使用其他的方式加密也是可以的,spring security提供了一下幾種加密
2、測試介面訪問時提示沒有許可權。
Spring Security預設的角色字首是”ROLE_”,使用hasRole方法時已經預設加上了,因此我們在資料庫裡面的使用者角色應該是“ROLE_user”,在user前面加上”ROLE_”字首