SpringSecurity 是Spring家族的一员,是Spring家族提供的安全框架,提供认证和授权功能,最主要的是它提供了简单的使用方式,同时又有很高的灵活性,简单,灵活,强大。
主要的主要的几个配置类
自定义Security的策略
AuthorizationServerConfigurerAdapter
例子如下:
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.tokenGranter(new CompositeTokenGranter(getTokenGranters(endpoints)))
.userDetailsService(userDetailsService)
.tokenStore(tokenStore());
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
//存放到db
clients.jdbc(dataSource);
// 存放到内存
clients.inMemory()
.withClient("webapp")
.secret(pass)
.scopes("server")
.authorizedGrantTypes("password", "authorization_code", "refresh_token")
.accessTokenValiditySeconds(24 * 60 * 60) //token有效期
.refreshTokenValiditySeconds(24 * 60 * 60)
.and().withClient("app")
.secret(pass)
.scopes("app")
.authorizedGrantTypes("authorization_code", "refresh_token")
.redirectUris("https://www.baidu.com/");
}
/**
* 填充认证方式
* @param endpoints endpoints
* @return list
*/
private List<TokenGranter> getTokenGranters(AuthorizationServerEndpointsConfigurer endpoints) {
AuthorizationServerTokenServices tokenServices = endpoints.getTokenServices();
ClientDetailsService clientDetailsService = endpoints.getClientDetailsService();
OAuth2RequestFactory oAuth2RequestFactory = endpoints.getOAuth2RequestFactory();
AuthorizationCodeServices authorizationCodeServices = endpoints.getAuthorizationCodeServices();
return new ArrayList<>(Arrays.asList(
new RefreshTokenGranter(tokenServices, clientDetailsService, oAuth2RequestFactory),
new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetailsService,
oAuth2RequestFactory),
new ResourceOwnerPasswordTokenGranter(authenticationManager, endpoints.getTokenServices(),
clientDetailsService, oAuth2RequestFactory),
new MyAbstractTokenGranter(authenticationManager, tokenServices, clientDetailsService,
oAuth2RequestFactory)));
}
}
默认情况下spring security oauth 的http配置。
ResourceServerConfigurerAdapter:
例子如下
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.authorizeRequests()
.antMatchers("/index/getUser").permitAll()
.antMatchers("/user/getUserPassword").permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
}
WebSecurityConfigurerAdapter 类是个适配器, 需要配置的时候需要我们自己写个配置类去继承,根据自己的需求去进行配置即可WebSecurityConfigurerAdapter的配置拦截要优先于ResourceServerConfigurerAdapter,优先级高的http配置是可以覆盖优先级低的配置的。
WebSecurityConfigurerAdapter
例子如下:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自定义实现用户信息获取
*
* @return
*/
@Bean
@Override
public UserDetailsService userDetailsService() {
return new MyUserDetailsServiceImpl();
}
/**
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService())
.passwordEncoder(passwordEncoder());
}
/**
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic();
}
/**
* 不定义没有password grant_type
*/
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 密码解析器
* @return PasswordEncoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
继承了抽象类 AbstractTokenGranter
AbstractTokenGranter
public class MyAbstractTokenGranter extends AbstractTokenGranter {
@Autowired
private UserInfo userInfo;
@Autowired
private RedisUtil redisUtil;
private static final String GRANT_TYPE = "sms_cod";
private final AuthenticationManager authenticationManager;
public MyAbstractTokenGranter(AuthenticationManager authenticationManager,
AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService,
OAuth2RequestFactory requestFactory) {
this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
}
protected MyAbstractTokenGranter(AuthenticationManager authenticationManager,
AuthorizationServerTokenServices tokenServices,
ClientDetailsService clientDetailsService,
OAuth2RequestFactory requestFactory, String grantType) {
super(tokenServices, clientDetailsService, requestFactory, grantType);
this.authenticationManager = authenticationManager;
}
/**
* 我们拿到的 token 终会过期的, 对应于刷新 token模式的 RefreshTokenGranter 则负责获取新的 OAuth2AccessToken。
*
* @param client
* @param tokenRequest
* @return
*/
@Override
protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
// 传入的参数需要有 refresh_token (DefaultOAuth2AccessToken 中有 refreshToken 字段)
String refreshToken = tokenRequest.getRequestParameters().get("refresh_token");
// 调用 tokenService 的刷新方法得到新的 OAuth2AccessToken
return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
}
/**
* 用户拦截
*
* @param client client
* @param tokenRequest 请求的令牌
* @return
*/
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
String username = parameters.get("username");
String password = parameters.get("password");
String mobile = parameters.get("mobile");
//客户端提交的验证码
String smscode = parameters.get("sms_cod");
String type = parameters.get("type");
parameters.remove("password");
//验证用户状态(是否警用等)
// UserInfo userInfo = new UserInfo();
// userInfo.setMobile(mobile);
// 验证验证码
//获取服务中保存的用户验证码
String smsCheckCode = (String) redisUtil.get(userInfo.getMobile());
if (StringUtils.isBlank(smsCheckCode)) {
throw new InvalidGrantException("用户没有发送验证码");
}
if (!smscode.equals(smsCheckCode)) {
throw new InvalidGrantException("验证码不正确");
} else {
//验证通过后从缓存中移除验证码
redisUtil.setRemove(userInfo.getMobile(), smsCheckCode);
}
if (username.isEmpty()) {
throw new InvalidGrantException("用户不存在");
}
Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
((AbstractAuthenticationToken) userAuth).setDetails(parameters);
try {
userAuth = authenticationManager.authenticate(userAuth);
} catch (AccountStatusException ase) {
//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
throw new InvalidGrantException(ase.getMessage());
} catch (BadCredentialsException e) {
// If the username/password are wrong the spec says we should send 400/invalid grant
throw new InvalidGrantException(e.getMessage());
}
if (userAuth == null || !userAuth.isAuthenticated()) {
throw new InvalidGrantException("Could not authenticate user: " + username);
}
OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, userAuth);
}
核心依赖配置配置如下:
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>${oauth2.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
<exclusions>
<!--旧版本 redis操作有问题-->
<exclusion>
<artifactId>spring-security-oauth2</artifactId>
<groupId>org.springframework.security.oauth</groupId>
</exclusion>
</exclusions>
使用SpringSrcurity+Anut2根据token实现用户密码登录
service层
/**
* 根据用户名查询
* @param userName 用户名
* @return 返回的路径
*/
UserInfo getUserInfoByName(@Param("userName") String userName);
Service层
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {
@Autowired
public PasswordEncoder passwordEncoder;
@Resource
private UserInfoService userInfoService;
@Resource
private UserInfoMapper userInfoMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserInfo userInfo = userInfoService.getUserInfoByName(username);
if (userInfo == null){
throw new UsernameNotFoundException(username);
}
return userInfo;
}
Controlleer层
@RestController
@RequestMapping("/")
public class IndexController {
/**
* user
* @param user user
* @return Principal
*/
@GetMapping(value = "/user")
public Principal user(Principal user) {
return user;
}