Now Spring boot greatly simplifies the configuration of Spring Framework applications, and the basic security of the program can be achieved through simple dependency configuration.
First let's take a look at oauth 2.0
The specification of OAuth 2.0 can refer to: RFC 6749
OAuth is an open standard that allows a user to allow third-party applications to access private resources (such as photos, videos, contact lists) stored by the user on a website without providing the user name and password to the third-party application. Currently, the latest version of OAuth is 2.0
OAuth allows users to provide a token instead of a username and password to access their data stored with a specific service provider. Each token authorizes a specific website (eg, a video editing website) to access a specific resource (eg, just a video in a certain album) for a specified period of time (eg, within the next 2 hours). In this way, OAuth allows users to authorize third-party websites to access information they store on another service provider without sharing their access permissions or the entirety of their data.
- resource owner: resource owner, referring to the "user" of the terminal
- resource server: A resource server, that is, a service provider that stores protected resources. To access these resources, you need to obtain an access token (access token). It and the authentication server can be the same server or a different server. If we visit the Sina Blog website, then if we use the Sina Blog account to log in to the Sina Blog website, then the resources of Sina Blog and the authentication of Sina Blog are the same, which can be considered to be the same server. If we log in to Zhihu with a Sina blog account, then obviously Zhihu's resources and Sina's authentication are not the same server.
- client: A client that represents a third-party application that makes resource requests to protected resources.
- authorization server: The authorization server will issue an access token to the client after verifying the resource owner and obtaining authorization.
In our system we need to add dependency library.
· spring-boot-starter-web
· spring-boot-starter-security
· spring-boot-starter-web
· spring-security-jwt
· spring-security-oauth2
The main directory structure of the configuration is like this
First configure Spring boot's configuration file application.properties
security.oauth2.resource.filter-order=3
security.signing-key=MaYzkSjmkzPC57L
security.security-name=fmanager
security.jwt.client-id=testjwtclientid
security.jwt.client-secret=XY7kmzoNzl100
security.jwt.client-role=ADMIN_USER
security.jwt.grant-type=password
security.jwt.scope-read=read
security.jwt.scope-write=write
security.jwt.resource-ids=testjwtresourceid
#需要的是上面部分
# LOGGING
logging.level.org.springframework.web=DEBUG
logging.level.main.java.com.fmanager = DEBUG
spring.datasource.url=jdbc:postgresql://localhost:5432/fmanager
spring.datasource.username=xxxxx
spring.datasource.password=xxxx
mybatis.config-locations=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
security configuration,
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${security.signing-key}")
private String signingKey;
@Value("${security.security-name}")
private String securityRealm;
@Resource
private UserDetailsService userDetailsService;
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(new BCryptPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic()
.realmName(securityRealm)
.and()
.csrf()
.disable().authorizeRequests().antMatchers("/oauth/token").permitAll(); //$NON-NLS-1$
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(signingKey);
return converter;
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
@Primary //Making this primary to avoid any accidental duplication with another token service instance of the same name
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
- Note to add
- JwtTokenStore and JwtAccessTokenConverter beans: JwtAccessTokenConverter is to be used in authorization server and resource server, JwtTokenStore is used to decode token in resource server.
- UserDetailsService: We inject our own UserDetailsServiceImpl (click to view) service, so that we can achieve authentication through username and password
- Password encryption uses BCryptPasswordEncoder provided by spring
Authorization server configuration
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Value("${security.jwt.client-id}")
private String clientId;
@Value("${security.jwt.client-secret}")
private String clientSecret;
@Value("${security.jwt.grant-type}")
private String grantType;
@Value("${security.jwt.scope-read}")
private String scopeRead;
@Value("${security.jwt.scope-write}")
private String scopeWrite = "write"; //$NON-NLS-1$
@Value("${security.jwt.resource-ids}")
private String resourceIds;
@Autowired
private TokenStore tokenStore;
@Autowired
private JwtAccessTokenConverter accessTokenConverter;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer.inMemory().withClient(clientId).secret(passwordEncoder.encode(clientSecret))
.authorizedGrantTypes(grantType).scopes(scopeRead, scopeWrite).resourceIds(resourceIds);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
endpoints.tokenStore(tokenStore).accessTokenConverter(accessTokenConverter).tokenEnhancer(enhancerChain)
.authenticationManager(authenticationManager);
}
@Bean
private static DelegatingPasswordEncoder passwordEncoder() {
String idForEncode = "bcrypt"; //$NON-NLS-1$
Map<String, PasswordEncoder> encoderMap = new HashMap<>();
encoderMap.put(idForEncode, new BCryptPasswordEncoder());
return new DelegatingPasswordEncoder(idForEncode, encoderMap);
}
}
- Inherit AuthorizationServerConfigurerAdapter and implement the configuration inside
- Client id, Client secret, provide authentication client id and password
- scope : defines the access resource level
- AuthenticationManager : Spring's authentication manager
Resource server configuration
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private ResourceServerTokenServices tokenServices;
@Value("${security.jwt.resource-ids}")
private String resourceIds;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceIds).tokenServices(tokenServices);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.and()
.authorizeRequests()
.antMatchers("/**").authenticated(); //$NON-NLS-1$
}
}
- Configure resource access in the configure method, here I configured all urls to require authentication
The configuration is basically completed, and the configuration of connecting to the database is also very simple. Add mybastis configuration and spring connection configuration in application.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/fmanager
spring.datasource.username=xxxxx
spring.datasource.password=xxxx
mybatis.config-locations=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
Just write the corresponding mapper
for example:
@Mapper
public interface UserDAO {
public User findById(long id);
public User findByUserName(String name);
}
- Note that the annotation (Mapper) should be added, so that the system will not be automatically loaded
Well, first write a test Controller
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
private UserServcie userService;
@RequestMapping(value="/greeting",method = RequestMethod.GET)
public String greeting() {
System.out.println(userService.findById(1));
return "Hello";
}
}
Access with rest client will see
permission denied.
Here we use the username and password to get the token
To get this token, remember to add
Content-Type:application/x-www-form-urlencoded
authorization: Basic dGVzdGp3dGNsaWVudGlkOlhZN2ttem9OemwxMDA=
This authorization is obtained through the previous client_id and client_secret base64.
get this token,
Then we can access the test controller with this token when we visit the test controller again.