Spring-Security
统一提供拦截器,接口实现用户权限验证,放行等安全工作。可整合OAuth2、JWT等验证模式。
此处为SpringBoot整合SpirngCouldOauth2实现JWT验证的实例
目录
- Maven依赖
- web安全配置
WebSecurityConfig
- User对象实现
UserDetails
配置验证
UserService
实现UserDetailsService
实现验证流程*
AuthorizationServerConfig
授权服务器 配置
JwtTokenStoreConfig
JWT存储配置
JwtTokenEnhancer
JWT内容扩展(可选)
ResourceServerConfig
资源服务器 配置
- 测试
1.Maven依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <properties> <java.version>1.8</java.version> <spring-cloud.version>Hoxton.SR9</spring-cloud.version> </properties>
<dependencies> <!--oauth2依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <!--security依赖--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-security</artifactId> </dependency> <!--jwt编码解码依赖--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
</dependencies>
<!-- spring-cloud 依赖 --> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
|
2.web安全配置WebSecurityConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package io.kid1999.esystem.config;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; 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;
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); }
@Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }
@Override protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests() .antMatchers("/**").permitAll(); }
}
|
3.User对象实现UserDetails
配置验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| package io.kid1999.esystem.entity;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails;
import java.io.Serializable; import java.time.LocalDateTime; import java.util.Collection;
@Data public class User implements Serializable, UserDetails {
@TableId(type = IdType.AUTO) private Long id; private String username; private String password;
@Override public Collection<? extends GrantedAuthority> getAuthorities() { return null; }
@Override public String getPassword() { return this.password; }
@Override public String getUsername() { return this.username; }
@Override public boolean isAccountNonExpired() { return false; }
@Override public boolean isAccountNonLocked() { return false; }
@Override public boolean isCredentialsNonExpired() { return false; }
@Override public boolean isEnabled() { return false; } }
|
4.UserService实现UserDetailsService
实现验证流程*
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| package io.kid1999.esystem.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import io.kid1999.esystem.dao.UserDao; import io.kid1999.esystem.entity.User; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.SimpleGrantedAuthority; 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.Component;
import java.util.ArrayList; import java.util.List;
@Slf4j @Component public class UserService implements UserDetailsService {
@Autowired private UserDao userDao;
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { log.info("login " + username); QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("username",username); User user = userDao.selectOne(wrapper); List<SimpleGrantedAuthority> authorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority("ROLE_user")); if(user == null){ throw new UsernameNotFoundException("用户不存在!"); }else{ String password = user.getPassword(); return new org.springframework.security.core.userdetails.User(username,password,authorities); } }
}
|
5.AuthorizationServerConfig 授权服务器 配置
验证模式、数据存储、校验信息、放行规则….
此处的 client-id 和 client-secret、访问权限scope 都是自己设置的后面会用到!!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| package io.kid1999.esystem.config;
import io.kid1999.esystem.common.Constants; import io.kid1999.esystem.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.password.PasswordEncoder; 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.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; 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.JwtAccessTokenConverter;
import java.util.ArrayList; import java.util.List;
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired private PasswordEncoder passwordEncoder; @Autowired private UserService userService; @Autowired private AuthenticationManager authenticationManager; @Autowired private TokenStore jwtTokenStore; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired private JwtTokenEnhancer jwtTokenEnhancer;
@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints){ TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); List<TokenEnhancer> list = new ArrayList<>(); list.add(jwtTokenEnhancer); list.add(jwtAccessTokenConverter); tokenEnhancerChain.setTokenEnhancers(list);
endpoints.authenticationManager(authenticationManager) .userDetailsService(userService) .tokenStore(jwtTokenStore) .accessTokenConverter(jwtAccessTokenConverter) .tokenEnhancer(tokenEnhancerChain); }
@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient(Constants.CLIENT_ID) .secret(passwordEncoder.encode(Constants.CLIENT_SECRET)) .accessTokenValiditySeconds(Constants.TOKEN_EXPIRE_DATE) .scopes("all") .authorizedGrantTypes("refresh_token", "authorization_code","password"); }
@Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.allowFormAuthenticationForClients(); security.checkTokenAccess("isAuthenticated()"); security.tokenKeyAccess("isAuthenticated()");
} }
|
6.JwtTokenStoreConfig JWT存储配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package io.kid1999.esystem.config;
import io.kid1999.esystem.common.Constants; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
@Configuration public class JwtTokenStoreConfig {
@Bean public TokenStore jwtTokenStore(){ return new JwtTokenStore(jwtAccessTokenConverter()); }
@Bean public JwtAccessTokenConverter jwtAccessTokenConverter(){ JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); accessTokenConverter.setSigningKey(Constants.TOKEN_SECRET); accessTokenConverter.setVerifierKey(Constants.TOKEN_SECRET); return accessTokenConverter; }
@Bean public JwtTokenEnhancer jwtTokenEnhancer(){ return new JwtTokenEnhancer(); } }
|
7.JwtTokenEnhancer JWT内容扩展(可选)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package io.kid1999.esystem.config;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import java.util.HashMap; import java.util.Map;
public class JwtTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) { Map<String,Object> info = new HashMap<>(); info.put("test","infomation"); ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(info); return oAuth2AccessToken; } }
|
8.ResourceServerConfig 资源服务器 配置
此处的放行地址需自行按序配置、配置访问地址有三次机会:webconfig
、此处、注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| package io.kid1999.esystem.config;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; 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.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.TokenStore;
@Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired private TokenStore jwtTokenStore;
@Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.tokenStore(jwtTokenStore); }
@Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/user/login","/user/register","/user/*").permitAll() .antMatchers("/oauth/**").permitAll() .antMatchers("/swagger-ui.html").permitAll() .antMatchers("/swagger-resources/**").permitAll() .antMatchers("/images/**").permitAll() .antMatchers("/webjars/**").permitAll() .antMatchers("/v2/api-docs").permitAll() .antMatchers("/configuration/ui").permitAll() .antMatchers("/configuration/security").permitAll() .anyRequest().authenticated();
}
}
|
9.测试
1.在IDEA中可以看到如下的接口,这是OAuth2模式下自带的验证接口
2.在postman中验证接口获取access_token 即为JWT
参数为:
1 2 3 4 5 6 7 8
| # 校验模式 grant_type:password # 用户名 username:1111 # 密码 password:1111 # 适用范围,这个在校验中配置AuthorizationServerConfig scope:all
|
Auth模式为 Basic Auth
此处的username 就是 JWT的Clint-id
此处的password就是 JWT的Clint-secret
{Authorization: Basic base64(Clint-id:Clint-secret)}
3.在postman中携带JWT获取数据
{Authorization: bearer JWT}