参考文章Spring Security Oauth2关于自定义登录的几种解决方案(一)_小丑竟是我自己-CSDN博客
初衷
由于oauth2默认提供的password模式需要client_id,秘钥,grant_type,用户名,密码这几个参数才可以进行登录验证。
有没有一种方式,只输入用户名,密码就能登录的呢?
代码实现
目录
初衷
代码实现
新建用户名密码权限认证类
新建oauth核心配置类
新建登录方法
配置客户端
测试
总结
导入oauth包
4.0.0 com.itcv.spring.oauth.demo spring-oauth-demo1.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent2.2.10.RELEASE org.springframework.boot spring-boot-starter-actuatororg.springframework.boot spring-boot-starter-weborg.springframework.boot spring-boot-starter-freemarker2.3.12.RELEASE org.springframework.cloud spring-cloud-starter-oauth22.2.0.RELEASE spring-security-oauth2 org.springframework.security.oauth org.springframework.security.oauth spring-security-oauth22.3.8.RELEASE com.alibaba fastjson1.2.78 org.projectlombok lombok
新建用户名密码权限认证类
@Component
public class AdminPwdAuthenticationProvider implements AuthenticationProvider {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserDetailsService userDetailsService;
//认证方法
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken adminLoginToken = (UsernamePasswordAuthenticationToken) authentication;
System.out.println("===进入Admin密码登录验证环节====="+ JSON.toJSonString(adminLoginToken));
UserDetails userDetails = userDetailsService.loadUserByUsername(adminLoginToken.getName());
//matches方法,前面为明文,后续为加密后密文
//匹配密码。进行密码校验
if(passwordEncoder.matches(authentication.getCredentials().toString(),userDetails.getPassword())){
return new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities());
}
throw new BadCredentialsException("用户名密码不正确");
}
@Override
public boolean supports(Class> authentication) {
return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
}
}
新建oauth核心配置类
@Configuration
@EnableWebSecurity
@EnableAuthorizationServer //这个配置是必须加的,不加的话,接下来使用TokenEndpoint会提示找不到对应的类
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] excludedAuthPages = {"/login/**",};
@Override
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
@Override
//模拟用户
public UserDetailsService userDetailsService() {
// 测试方便采用内存存取方式
InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
userDetailsService.createUser(User.withUsername("user_1").password(passwordEncoder().encode("123456")).authorities("ROLE_USER").build());
userDetailsService.createUser(User.withUsername("user_2").password(passwordEncoder().encode("1234567")).authorities("ROLE_USER").build());
return userDetailsService;
}
@Autowired
private AdminPwdAuthenticationProvider adminPwdAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//此处注入一个默认的service,也可以不注入,我做测试时使用,根据需求,该service非admin-userservice
auth.userDetailsService(userDetailsService());
//添加两个Provider
// auth.authenticationProvider(adminSmsAuthenticationProvider);
auth.authenticationProvider(adminPwdAuthenticationProvider);
}
//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().contentTypeOptions().disable()
.frameOptions().sameOrigin()
.and()
.authorizeRequests()
.antMatchers(excludedAuthPages).permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
.and().authorizeRequests()
.anyRequest().authenticated()
.and().formLogin().disable()
.exceptionHandling()
.and().csrf().disable()
.logout().disable().authorizeRequests();
}
}
新建登录方法
@RestController
@RequestMapping("/")
public class LoginController {
@Autowired
private OAuth2ClientProperties oauth2ClientProperties;
@Autowired
private TokenEndpoint tokenEndpoint;
@PostMapping("/login/admin")
public OAuth2AccessToken adminLogin(@RequestBody UserRequest request) throws HttpRequestMethodNotSupportedException {
//创建客户端信息,客户端信息可以写死进行处理,因为Oauth2密码模式,客户端双信息必须存在,所以伪装一个
//如果不想这么用,需要重写比较多的代码
User clientUser= new User(oauth2ClientProperties.getClientId(),oauth2ClientProperties.getClientSecret(), new ArrayList<>());
//生成已经认证的client
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(clientUser,null, new ArrayList<>());
Map parameters = new HashMap();
//封装成一个UserPassword方式的参数体,放入手机号
parameters.put("username", request.getPhone());
//放入验证码
parameters.put("password", request.getVcode());
//授权模式为:密码模式
parameters.put("grant_type", "password");
//调用自带的获取token方法。
OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(token, parameters).getBody();
return oAuth2AccessToken;
}
配置客户端
security:
oauth2:
client:
clientId: demoApp
clientSecret: 123456
scope: ALL
authorized-grant-types: password,refresh_token
测试
@Configuration
@EnableWebSecurity
@EnableAuthorizationServer //这个配置是必须加的,不加的话,接下来使用TokenEndpoint会提示找不到对应的类
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] excludedAuthPages = {"/login/**",};
@Override
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
@Bean
@Override
//模拟用户
public UserDetailsService userDetailsService() {
// 测试方便采用内存存取方式
InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
userDetailsService.createUser(User.withUsername("user_1").password(passwordEncoder().encode("123456")).authorities("ROLE_USER").build());
userDetailsService.createUser(User.withUsername("user_2").password(passwordEncoder().encode("1234567")).authorities("ROLE_USER").build());
return userDetailsService;
}
@Autowired
private AdminPwdAuthenticationProvider adminPwdAuthenticationProvider;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//此处注入一个默认的service,也可以不注入,我做测试时使用,根据需求,该service非admin-userservice
auth.userDetailsService(userDetailsService());
//添加两个Provider
// auth.authenticationProvider(adminSmsAuthenticationProvider);
auth.authenticationProvider(adminPwdAuthenticationProvider);
}
//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().contentTypeOptions().disable()
.frameOptions().sameOrigin()
.and()
.authorizeRequests()
.antMatchers(excludedAuthPages).permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll()
.and().authorizeRequests()
.anyRequest().authenticated()
.and().formLogin().disable()
.exceptionHandling()
.and().csrf().disable()
.logout().disable().authorizeRequests();
}
}
新建登录方法
@RestController
@RequestMapping("/")
public class LoginController {
@Autowired
private OAuth2ClientProperties oauth2ClientProperties;
@Autowired
private TokenEndpoint tokenEndpoint;
@PostMapping("/login/admin")
public OAuth2AccessToken adminLogin(@RequestBody UserRequest request) throws HttpRequestMethodNotSupportedException {
//创建客户端信息,客户端信息可以写死进行处理,因为Oauth2密码模式,客户端双信息必须存在,所以伪装一个
//如果不想这么用,需要重写比较多的代码
User clientUser= new User(oauth2ClientProperties.getClientId(),oauth2ClientProperties.getClientSecret(), new ArrayList<>());
//生成已经认证的client
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(clientUser,null, new ArrayList<>());
Map parameters = new HashMap();
//封装成一个UserPassword方式的参数体,放入手机号
parameters.put("username", request.getPhone());
//放入验证码
parameters.put("password", request.getVcode());
//授权模式为:密码模式
parameters.put("grant_type", "password");
//调用自带的获取token方法。
OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(token, parameters).getBody();
return oAuth2AccessToken;
}
配置客户端
security:
oauth2:
client:
clientId: demoApp
clientSecret: 123456
scope: ALL
authorized-grant-types: password,refresh_token
测试
security: oauth2: client: clientId: demoApp clientSecret: 123456 scope: ALL authorized-grant-types: password,refresh_token
测试
访问自定义登录接口,输入用户名,密码可以正常登录
访问oauth2自带登录接口
总结
违背了初衷,原本想着是两个都可以正常使用的;oauth2自带的登录接口也走到了自定义的类里边。
源码地址
spring-security-demo: spring-sevurity入门实例



