How does Oauth2 use password policy to complete authorization?
I. Importing Relevant Dependencies
<!-- oauth2 Dependent dependence -->
<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>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Database Access Dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!-- Test-related dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
server:
port: 8092
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/token?serverTimezone=Asia/Shanghai&useSSL=false
username: root
password: 123456
# JPA configuration
jpa:
show-sql: true
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect #If not, the default is myisam engine
database: mysql
#security:
# oauth2:
# resource:
# ####Verify token from the Authentication Authority
# tokenInfoUri: http://localhost:8092/oauth/check_token
# preferTokenInfo: true
# client: ## Get the access token address
# accessTokenUri: http://localhost:8092/oauth/token
# userAuthorizationUri: http://localhost:8092/oauth/authorize# authorized address
# clientId: client
# clientSecret: client
II. Project structure
Configuration classes of oauth2
3.1. Authorization Server Config
/**
* Authentication Server
*/
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private DomainUserDetailsService userDetailsService;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security);
}
/**
* Client Configuration (to whom tokens are issued)
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("client")
.secret(passwordEncoder.encode("client"))
//Effective time: 2 hours
.accessTokenValiditySeconds(2*60*60)
//Password authorization mode and refresh token
.authorizedGrantTypes(new String[]{"refresh_token", "password"})
.scopes( "all");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints){
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService);
}
}
3.2. Resource Server Configurer
/**
* Resource Service Allocation
*
* @ EnableResourceServer Enabling Resource Services
* @ EnableWebSecurity Enabling web Security
* @ EnableGlobalMethodSecurity With global method security annotations enabled, you can use annotations in methods to filter requests
*/
@Configuration
@EnableResourceServer//Open Resource Services
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
// Intercept api/order requests to verify the relationship between accessToken and controller
http.authorizeRequests()
//Path of release
.antMatchers(Constant.IGNORE_PATHS
.toArray(new String[Constant.IGNORE_PATHS.size()])
).permitAll()
.anyRequest().authenticated()
.and()
.httpBasic().and().csrf().disable();//Cross-domain issues with closing open csrf protection
}
}
3.3. Security Config (Open Web Sercurity Function)
package com.yonyou.oauth.config;
import com.yonyou.oauth.DomainUserDetailsService;
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.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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.client.RestTemplate;
/**
* Security configuration
*
* @ EnableWebSecurity Enabling web Security Configuration
* @ EnableGlobalMethodSecurity With global method security annotations enabled, you can use annotations in methods to filter requests
*/
@Configuration
@EnableWebSecurity//Turn on the Web Security function
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
3.4. Constant (defining some API s or paths for negligible authorization)
/**
* constant
*/
public class Constant {
/**
* Define paths that can be accessed without authorization
*/
public static final List<String> IGNORE_PATHS;
static {
IGNORE_PATHS = new ArrayList<>();
//Access Path and Resources of swagger2
IGNORE_PATHS.add("/v2/api-docs");
IGNORE_PATHS.add("/swagger-ui.html");
IGNORE_PATHS.add("/webjars/**");
}
}
IV. Verification of Password Policy
DomainUserDetailsService class
/**
* User Information Service
* Implementation of Spring Security User Details Service Interface Method for Identity Authentication
*/
@Service
public class DomainUserDetailsService implements UserDetailsService {
@Autowired
//AccountService here is the service layer that finds the corresponding data class from the database
private AccountService accountService;
/**
* Find account information according to user name and return user information entity
* @param username User name
* @return User Details User Information Entities for Identity Authentication
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
String password = null;
System.out.println("The requested authorization account number is:"+username);
//Find the user's password from the database (if it exists)
password = accountService.getPasswordByName(username);
if(password == null){
throw new RuntimeException("Account does not exist, authorization failed!");
}
System.out.println("The password queried is:"+password);
//Verify
return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
}
5. Classes of query databases used
5.1. Entity class
package com.yonyou.entity;
import lombok.Data;
import javax.persistence.*;
@Entity
@Table(name = "account")
@Data
public class Account {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(columnDefinition = "varchar(20) NOT NULL comment 'User name'")
private String username;
@Column(columnDefinition = "varchar(100) NOT NULL comment 'Password'")
private String password;
}
5.2. AccountRepository class
public interface AccountRepository extends JpaRepository<Account, Integer> {
/**
* Search for passwords based on user names
* @param username
* @return
*/
@Query(nativeQuery = true, value = "select password from account where username=?1")
String getPasswordByName(String username);
}
5.3. AccountServiceImpl class
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountRepository accountRepository;
@Override
public String getPasswordByName(String username) {
return accountRepository.getPasswordByName(username);
}
}
6. Test class HelloController
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "HelloWorld";
}
}
VII. Test results
1. Obtaining authorization token
2, test
Eight, pay attention to
- User passwords in tables should be encrypted first by oauth2's unique encryption method, and then saved, otherwise they will be invalid.
- Test classes that simulate adding data
@RunWith(SpringRunner.class)
@SpringBootTest
public class Oauth2ApplicationTests {
@Autowired
private AccountRepository accountRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Test
public void contextLoads() {
Account account = new Account();
account.setUsername("admin");
String password = "123456";
//Use passwordEncoder to encrypt first
password = passwordEncoder.encode(password);
//Set values in
account.setPassword(password);
//Preservation
accountRepository.save(account);
}
}