打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
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.
*/
publicclassMyUserDetailsServiceimplementsUserDetailsService{
    @Autowired
    privateUserServiceuserService;
    @Autowired
    privateUserRoleServiceuserRoleService;
    /**
     * 根据用户名获取登录用户信息
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    publicUserDetailsloadUserByUsername(Stringusername)throwsUsernameNotFoundException{
        Useruser=userService.findByUsername(username);
        if(user==null){
            thrownewUsernameNotFoundException("用户名:"+username+"不存在!");
        }
        Collection<SimpleGrantedAuthority>collection=newHashSet<SimpleGrantedAuthority>();
        Iterator<String>iterator=  userRoleService.findRoles(user.getId()).iterator();
        while(iterator.hasNext()){
            collection.add(newSimpleGrantedAuthority(iterator.next()));
        }
        returnneworg.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,然后通过AuthenticationManagerBuilderuserDetailsService方法,设置进去就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
    publicvoidglobalUserDetails(AuthenticationManagerBuilderauth)throwsException{
        auth
.userDetailsService(myUserDetailsService())
.passwordEncoder(newMd5PasswordEncoder());
    }

代码中已经配置了密码使用MD5加密,所以使用demo时,插入到数据库的密码要使用MD5加密一样。
如果你想使用其他的方式加密也是可以的,spring security提供了一下几种加密

2、测试接口访问时提示没有权限。
Spring Security默认的角色前缀是”ROLE_”,使用hasRole方法时已经默认加上了,因此我们在数据库里面的用户角色应该是“ROLE_user”,在user前面加上”ROLE_”前缀


本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
认证鉴权与API权限控制在微服务架构中的设计与实现:升级 | Aoho''s Blog
写了一个 SSO 单点登录的代码示例给胖友!
Spring Boot 使用默认Redis客户端操作工具lettuce时,出现Caused by: io.lettuce.core.RedisCommandTimeoutException: Comm
Spring Boot 远程查看日志
Spring Cloud入门-Oauth2授权之基于JWT完成单点登录(Hoxton版本)
如何自定义一个starter,你学废了么?
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服