文章来源: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依赖
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
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配置
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配置
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配置
| @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、密码加密问题
| @Autowired publicvoidglobalUserDetails(AuthenticationManagerBuilderauth)throwsException{ auth .userDetailsService(myUserDetailsService()) .passwordEncoder(newMd5PasswordEncoder()); } |
代码中已经配置了密码使用MD5加密,所以使用demo时,插入到数据库的密码要使用MD5加密一样。
如果你想使用其他的方式加密也是可以的,spring security提供了一下几种加密
2、测试接口访问时提示没有权限。
Spring Security默认的角色前缀是”ROLE_”,使用hasRole方法时已经默认加上了,因此我们在数据库里面的用户角色应该是“ROLE_user”,在user前面加上”ROLE_”前缀
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请
点击举报。