security 入门教程推荐(此教程写的很基础,解释也比较详细,security入门优质教程)
社区 Spring Security 从入门到进阶系列教程
项目地址:https://github.com/athc/hippo
spring security
spring boot 整合 security
org.springframework.security.core.userdetails.User
org.springframework.security.core.userdetails.UserDetails
1 . 实现UserDetailsService的loadUserByUsername方法,作用是从数据库获取用户信息
//给自定义认证方式添加加密方式,在userDetailsService将密码交给security去验证,在认证管理中配置密码验证方式
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(userInfo.getAccount(), userInfo.getPassword(), roles);
}
2 . 实现AuthenticationProvider的authenticate方法根据UserDetails实现类获取用户信息进行用户密码,状态等相关验证
3 . 告诉security认证方式
/**
* 添加自定义登录到认证security管理
*
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//用户认证逻辑,这里可以只配置userAuthenticationProvider 剩下的逻辑判断在provider中完成,如果配置了userDetailsService会去验证密码
auth.authenticationProvider(userAuthenticationProvider)
//获取用户信息
// .userDetailsService(userDetailsService)
//密码加密方式
//.passwordEncoder(passwordEncoder());
}
4 . 访问资源控制,http.authorizeRequests()方法有多个子节点,每个macher按照他们的声明顺序执行,路径配置顺序有要求 ,匹配就返回.
hasAnyAuthority("USER")需要有USER权限才能访问;
hasAnyRole("ADMIN")会自动给ADMIN加上ROLE_前缀,需要有ROLE_ADMIN角色才能访问。
/**
* security 拦截路径
* http.authorizeRequests()方法有多个子节点,每个macher按照他们的声明顺序执行
* 路径配置顺序有要求 ,匹配就返回
*
* @param http
* @throws Exception
*/
@Override protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/security/login/**").permitAll()
.antMatchers("/security/user/**").hasAnyAuthority("USER")
.antMatchers("/security/role/**").hasAnyRole("ADMIN")
.anyRequest().authenticated()
.and()
.rememberMe()
.key("my-secret")
.rememberMeCookieName("my-cookie-name")
.tokenValiditySeconds(24 * 60 * 60)
.and()
.formLogin()
.and()
.logout()
.and()
.httpBasic()
;
// 在 UsernamePasswordAuthenticationFilter 前添加自定义过滤器 BeforeLoginFilter
http.addFilterBefore(new BeforeLoginFilter(), UsernamePasswordAuthenticationFilter.class);
}
整合oauth2
项目地址:https://github.com/athc/hippo/tree/master/oauth2-security
security-oauth2区分了客户端和用户。
5 . 实现ClientDetailsService的loadClientByClientId方法,实现客户端认证
6 . 配置认证server(@EnableAuthorizationServer)通过继承AuthorizationServerConfigurerAdapter配置认证oauth2自定义客户端和用户认证
//client认证
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
//token存储位置
.tokenStore(new InMemoryTokenStore())
//将web security配置的authenticationManager
.authenticationManager(authenticationManager)
//刷新token会用到userDetailsService
.userDetailsService(userDetailsService)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
//允许验证token 接口访问,单点登录会访问这个接口验证token是否有效
oauthServer.checkTokenAccess("permitAll()");
//加密方式
oauthServer.passwordEncoder(passwordEncoder());
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
}
7 . 修改security的资源控制,不拦截oauth2资源
@Override protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/oauth/*").permitAll()
.and().httpBasic()
;
}
/**
* 在这security中,把AuthenticationManager交给Spring,
* 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
8 . 配置资源server(@EnableResourceServer) 继承ResourceServerConfigurerAdapter配置oauth2资源控制
@Configuration
@EnableResourceServer
class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
//资源id和loadClientByClientId查询到的相匹配
resources.resourceId("API");
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
//必须认证过后才可以访问
.antMatchers("/security/user/**").hasAnyAuthority("USER")
.antMatchers("/security/role/**").hasAnyRole("ADMIN")
.anyRequest().permitAll()
;
}
}
/**
* oauth2 几种获取token方式 client 可用basic 方式传递
* refresh token: http://localhost:8013/oauth/token?grant_type=refresh_token&refresh_token=3680e51e-fbf4-417a-85d9-6a8205c14c0a&client_id=user&client_secret=123456
* client: http://localhost:8013/oauth/token?client_id=user&client_secret=123456&scope=read&grant_type=client_credentials
* password: http://localhost:8013/oauth/token?username=zhangsan&password=123456&grant_type=password&scope=read&client_id=user&client_secret=1234567
* authorization code: http://localhost:8013/oauth/authorize?response_type=code&client_id=code&redirect_uri=http://localhost:8013/security/login&scope=all
*/
整合oauth2 短信认证登录
项目地址:https://github.com/athc/hippo/tree/master/oauth-third
1 拦截器拦截短信验证登录
继承认证拦截器AbstractAuthenticationProcessingFilter,重写认证方法
获取到手机号验证码,组装认证认证的Token类,提交认证
流程大致为:
1 拦截短信认证登录,提交短信认证
2 短信认证逻辑处理,认证成功,提交认证信息
3 生成 token
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
//短信验证预处理
if (!RequestMethod.POST.name().equals(request.getMethod())) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
String mobile = request.getParameter("mobile").trim();
String code = request.getParameter("code").trim();
if (mobile.isEmpty() || code.isEmpty()) {
throw new VerifyException("mobile or code cant be null");
}
//组装认证参数
SmsToken authRequest = new SmsToken(mobile, code, new ArrayList<SimpleGrantedAuthority>());
setDetails(request, authRequest);
//提交authenticationManager 认证
return this.getAuthenticationManager().authenticate(authRequest);
}
security 配置短信认证 逻辑处理provider
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//密码认证
auth
.authenticationProvider(userAuthenticationProvider)
.userDetailsService(userDetailsService)
//密码验证方式
.passwordEncoder(new BCryptPasswordEncoder())
;
//sms认证
auth
.authenticationProvider(smsProvider)
.userDetailsService(userDetailsService)
;
}
类似 用户名密码认证的短信认证 provider
@Override public Authentication authenticate(Authentication authentication) {
//短信验证码逻辑处理
SmsToken token = (SmsToken) authentication;
String mobile = (String) token.getPrincipal();
String code = (String) token.getCredentials();
UserDetails user = userDetailsService.loadUserByUsername(mobile);
logger.info(code);
//fixme: 验证code
if (code != code) {
throw new CredentialsExpiredException("$code expired.");
}
//返回认证完成Token
return new SmsToken(user, null, user.getAuthorities());
}
//支持自定义Token
@Override public boolean supports(Class<?> authentication) {
return SmsToken.class.isAssignableFrom(authentication);
}
整合oauth2 三方认证登录
项目地址:https://github.com/athc/hippo/tree/master/oauth-third
参数注入到类
@Bean
@ConfigurationProperties("sina.client")
public AuthorizationCodeResourceDetails sina() {
return new AuthorizationCodeResourceDetails();
}
@Bean
@Qualifier("sinaResource")
@Primary
@ConfigurationProperties("sina.resource")
public ResourceServerProperties sinaResource() {
return new ResourceServerProperties();
}
三方登录请求拦截
private Filter ssoFilter() {
CompositeFilter filter = new CompositeFilter();
List<Filter> filters = new ArrayList<>();
OAuth2ClientAuthenticationProcessingFilter sinaFilter = new OAuth2ClientAuthenticationProcessingFilter("/login/sina");
OAuth2RestTemplate sinaTemplate = new OAuth2RestTemplate(sina(), oauth2ClientContext);
sinaFilter.setRestTemplate(sinaTemplate);
//自定义userInfo 获取
SinaUserInfoTokenServices sinaTokenServices = new SinaUserInfoTokenServices(sinaResource().getUserInfoUri(), sina().getClientId());
sinaTokenServices.setRestTemplate(sinaTemplate);
sinaFilter.setTokenServices(sinaTokenServices);
//认证成功处理
sinaFilter.setAuthenticationSuccessHandler(authSuccessHandler);
//获取到三方信息 自定义处理 存库等
sinaTokenServices.setAuthoritiesExtractor(new MyAuthoritiesExtractor());
//三方登录权限处理
sinaTokenServices.setPrincipalExtractor(new MyPrincipalExtractor());
filters.add(sinaFilter);
filter.setFilters(filters);
return filter;
}
将拦截器放到拦截链中
自定义token:
实现自定义token产生
public class UserTokenEnhancer implements TokenEnhancer {
@Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken result = (DefaultOAuth2AccessToken) accessToken;
//uuid 去掉 `-`
result.setValue(result.getValue().replace("-", ""));
result.setRefreshToken(new DefaultOAuth2RefreshToken(UUID.randomUUID().toString().replace("-", "")));
//todo: 这里可以自定义token数据结构
return result;
}
}
从获取的三方信息中 获取有用的信息
MyPrincipalExtractor implements PrincipalExtractor
三方认证登录获取的权限
MyAuthoritiesExtractor implements AuthoritiesExtractor
security+oauth2+sso demo地址:https://github.com/athc/ath-cloud -----用的kotlin写的单点登录demo
参考链接:
https://spring.io/guides/tutorials/spring-boot-oauth2/