spring-security-oauth2实现用户认证,客户端信息和用户信息存到数据库中,token使用比较频繁存到redis中
一、新建maven项目,
所需的依赖
org.springframework.security.oauth
spring-security-oauth2
2.3.2.RELEASE
org.springframework.cloud
spring-cloud-starter-security
org.springframework.boot
spring-boot-starter-data-redis
二、配置资源服务器和授权服务器
客户端信息和用户信息存到数据库中,token使用比较频繁存到redis中,
package com.example.demo.config;
import com.example.demo.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
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.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import javax.sql.DataSource;
import java.util.Arrays;
/**
* @author tudou
* @Date 2018/11/14
**/
@Configuration
public class OAuth2ServerConfig {
private static final String DEMO_RESOURCE_ID = "order";
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
// Since we want the protected resources to be accessible in the UI as well we need
// session creation to be allowed (it's disabled by default in 2.0.6)
// .sessionManagement()
// .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
// .and()
.requestMatchers().anyRequest()
.and()
.anonymous()
.and()
.authorizeRequests()
// .antMatchers("/product/**").access("#oauth2.hasScope('select') and hasRole('ROLE_USER')")
.antMatchers("/hi/**").authenticated();//配置order访问控制,必须认证过后才可以访问
// @formatter:on
}
}
@Configuration
@EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
RedisConnectionFactory redisConnectionFactory;
String finalSecret = new BCryptPasswordEncoder().encode("123456");
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//配置两个客户端,一个用于password认证一个用于client认证
// clients.inMemory()
// .withClient("client")
// .resourceIds(DEMO_RESOURCE_ID)
// .authorizedGrantTypes("password", "refresh_token")
// .scopes("all", "read", "write")
// .accessTokenValiditySeconds(7200)
// .refreshTokenValiditySeconds(10000)
// .authorities("password")
// .secret(finalSecret);
//这个地方指的是从jdbc查出数据来存储
clients.withClientDetails(clientDetails());
}
@Autowired
DataSource dataSource ;
@Autowired
UserDetailsServiceImpl userDetailsService;
@Bean
public ClientDetailsService clientDetails() {
return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
//refresh_token 需要 UserDetailsService is required
.userDetailsService(userDetailsService)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.tokenStore( tokenStore());
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
}
@Bean
public TokenStore tokenStore() {
// return new InMemoryTokenStore();
// 需要使用 redis 的话,放开这里
return new RedisTokenStore(redisConnectionFactory);
}
}
}
三、配置spring security
认证用户时从数据库中取用户
package com.example.demo.config;
import com.example.demo.service.impl.UserDetailsServiceImpl;
import com.netflix.discovery.converters.Auto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.sql.DataSource;
import java.beans.Encoder;
/**
* @author tudou
* @Date 2018/11/14
**/
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
DataSource dataSource ;
@Autowired
UserDetailsServiceImpl userDetailsService ;
@Bean
@Override
protected UserDetailsService userDetailsService(){
// JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
// manager.setDataSource(dataSource);
return userDetailsService;
// InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
// manager.createUser(User.withUsername("user_1").password(finalPassword).authorities("USER").build());
// manager.createUser(User.withUsername("user_2").password(finalPassword).authorities("USER").build());
// return manager;
}
@Bean
PasswordEncoder passwordEncoder(){
// return PasswordEncoderFactories.createDelegatingPasswordEncoder();
return new BCryptPasswordEncoder();
}
public static void main(String[] args) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// password 方案一:明文存储,用于测试,不能用于生产
// String finalPassword = "123456";
// password 方案二:用 BCrypt 对密码编码
String finalPassword = bCryptPasswordEncoder.encode("123456");
// password 方案三:支持多种编码,通过密码的前缀区分编码方式
// String finalPassword1 = "{bcrypt}"+bCryptPasswordEncoder.encode("123456");
System.out.println(finalPassword);
}
// String finalPassword = new BCryptPasswordEncoder().encode("123456");
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.csrf().disable();
http
.requestMatchers().antMatchers("/oauth/**","/login/**","/logout/**")
.and()
.authorizeRequests()
.antMatchers("/oauth/**").authenticated()
.and()
.formLogin().permitAll(); //新增login form支持用户登录及授权
// @formatter:on
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authenticationProvider());
}
@Bean
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider
= new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(passwordEncoder());
return authProvider;
}
}
重写UserDetailsService
package com.example.demo.service.impl;
import com.example.demo.entity.Users;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* 重写用户服务
*
* @author: ytzhou
* @date: 2018/9/19 14:17
* @version: v1.0
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserServiceImpl userService;
@Override
public UserDetails loadUserByUsername(String userName) {
Users user = userService.findOneByusername(userName);
if (null == user) {
throw new UsernameNotFoundException(userName);
}
List authorities = new ArrayList<>();
// for (SysRole role : sysUser.getRoleList()) {
// for (SysPermission permission : role.getPermissionList()) {
// authorities.add(new SimpleGrantedAuthority(permission.getCode()));
// }
// }
return new User(user.getUsername(),user.getPassword(), authorities);
}
}
四、配置资源服务器
package com.example.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
/*
* 提供 user 信息,所以 oauth2-server 也是一个Resource Server
* */
@Configuration
@EnableResourceServer
//@Order(3)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
}
项目结构
参考文章:http://blog.didispace.com/tags/Oauth2/
https://www.cnblogs.com/Irving/p/9306008.html
http://wiselyman.iteye.com/blog/2379419