参考文章Spring Security Oauth2关于自定义登录的几种解决方案(一)_小丑竟是我自己-CSDN博客
初衷
由于oauth2默认提供的password模式需要client_id,秘钥,grant_type,用户名,密码这几个参数才可以进行登录验证。
有没有一种方式,只输入用户名,密码就能登录的呢?
代码实现
目录
导入oauth包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itcv.spring.oauth.demo</groupId>
<artifactId>spring-oauth-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</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-freemarker</artifactId>
<version>2.3.12.RELEASE</version>
</dependency>
<!-- OAuth2 认证服务器-->
<!-- spring oauth2.0 (包含了spring security无需单独引入) 相关 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.0.RELEASE</version>
<exclusions>
<!--旧版本 redis操作有问题-->
<exclusion>
<artifactId>spring-security-oauth2</artifactId>
<groupId>org.springframework.security.oauth</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
新建用户名密码权限认证类
@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<String, String> parameters = new HashMap<String, String>();
//封装成一个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
测试
访问自定义登录接口,输入用户名,密码可以正常登录
访问oauth2自带登录接口
总结
违背了初衷,原本想着是两个都可以正常使用的;oauth2自带的登录接口也走到了自定义的类里边。
源码地址