security 入門教程推薦(此教程寫的很基礎(chǔ),解釋也比較詳細(xì),security入門優(yōu)質(zhì)教程)
社區(qū) Spring Security 從入門到進(jìn)階系列教程
項(xiàng)目地址:https://github.com/athc/hippo
spring security
spring boot 整合 security
org.springframework.security.core.userdetails.User
org.springframework.security.core.userdetails.UserDetails
1 . 實(shí)現(xiàn)UserDetailsService的loadUserByUsername方法,作用是從數(shù)據(jù)庫獲取用戶信息
//給自定義認(rèn)證方式添加加密方式,在userDetailsService將密碼交給security去驗(yàn)證,在認(rèn)證管理中配置密碼驗(yàn)證方式
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(userInfo.getAccount(), userInfo.getPassword(), roles);
}
2 . 實(shí)現(xiàn)AuthenticationProvider的authenticate方法根據(jù)UserDetails實(shí)現(xiàn)類獲取用戶信息進(jìn)行用戶密碼,狀態(tài)等相關(guān)驗(yàn)證
3 . 告訴security認(rèn)證方式
/**
* 添加自定義登錄到認(rèn)證security管理
*
*/
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//用戶認(rèn)證邏輯,這里可以只配置userAuthenticationProvider 剩下的邏輯判斷在provider中完成,如果配置了userDetailsService會(huì)去驗(yàn)證密碼
auth.authenticationProvider(userAuthenticationProvider)
//獲取用戶信息
// .userDetailsService(userDetailsService)
//密碼加密方式
//.passwordEncoder(passwordEncoder());
}
4 . 訪問資源控制,http.authorizeRequests()方法有多個(gè)子節(jié)點(diǎn),每個(gè)macher按照他們的聲明順序執(zhí)行,路徑配置順序有要求 ,匹配就返回.
hasAnyAuthority("USER")需要有USER權(quán)限才能訪問;
hasAnyRole("ADMIN")會(huì)自動(dòng)給ADMIN加上ROLE_前綴,需要有ROLE_ADMIN角色才能訪問。
/**
* security 攔截路徑
* http.authorizeRequests()方法有多個(gè)子節(jié)點(diǎn),每個(gè)macher按照他們的聲明順序執(zhí)行
* 路徑配置順序有要求 ,匹配就返回
*
* @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
項(xiàng)目地址:https://github.com/athc/hippo/tree/master/oauth2-security
security-oauth2區(qū)分了客戶端和用戶。
5 . 實(shí)現(xiàn)ClientDetailsService的loadClientByClientId方法,實(shí)現(xiàn)客戶端認(rèn)證
6 . 配置認(rèn)證server(@EnableAuthorizationServer)通過繼承AuthorizationServerConfigurerAdapter配置認(rèn)證oauth2自定義客戶端和用戶認(rèn)證
//client認(rèn)證
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
//token存儲(chǔ)位置
.tokenStore(new InMemoryTokenStore())
//將web security配置的authenticationManager
.authenticationManager(authenticationManager)
//刷新token會(huì)用到userDetailsService
.userDetailsService(userDetailsService)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
//允許驗(yàn)證token 接口訪問,單點(diǎn)登錄會(huì)訪問這個(gè)接口驗(yàn)證token是否有效
oauthServer.checkTokenAccess("permitAll()");
//加密方式
oauthServer.passwordEncoder(passwordEncoder());
//允許表單認(rèn)證
oauthServer.allowFormAuthenticationForClients();
}
7 . 修改security的資源控制,不攔截oauth2資源
@Override protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/oauth/*").permitAll()
.and().httpBasic()
;
}
/**
* 在這security中,把AuthenticationManager交給Spring,
* 這一步的配置是必不可少的,否則SpringBoot會(huì)自動(dòng)配置一個(gè)AuthenticationManager,覆蓋掉內(nèi)存中的用戶
*/
@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()
//必須認(rèn)證過后才可以訪問
.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 短信認(rèn)證登錄
項(xiàng)目地址:https://github.com/athc/hippo/tree/master/oauth-third
1 攔截器攔截短信驗(yàn)證登錄
繼承認(rèn)證攔截器AbstractAuthenticationProcessingFilter,重寫認(rèn)證方法
獲取到手機(jī)號(hào)驗(yàn)證碼,組裝認(rèn)證認(rèn)證的Token類,提交認(rèn)證
流程大致為:
1 攔截短信認(rèn)證登錄,提交短信認(rèn)證
2 短信認(rèn)證邏輯處理,認(rèn)證成功,提交認(rèn)證信息
3 生成 token
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
//短信驗(yàn)證預(yù)處理
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");
}
//組裝認(rèn)證參數(shù)
SmsToken authRequest = new SmsToken(mobile, code, new ArrayList<SimpleGrantedAuthority>());
setDetails(request, authRequest);
//提交authenticationManager 認(rèn)證
return this.getAuthenticationManager().authenticate(authRequest);
}
security 配置短信認(rèn)證 邏輯處理provider
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
//密碼認(rèn)證
auth
.authenticationProvider(userAuthenticationProvider)
.userDetailsService(userDetailsService)
//密碼驗(yàn)證方式
.passwordEncoder(new BCryptPasswordEncoder())
;
//sms認(rèn)證
auth
.authenticationProvider(smsProvider)
.userDetailsService(userDetailsService)
;
}
類似 用戶名密碼認(rèn)證的短信認(rèn)證 provider
@Override public Authentication authenticate(Authentication authentication) {
//短信驗(yàn)證碼邏輯處理
SmsToken token = (SmsToken) authentication;
String mobile = (String) token.getPrincipal();
String code = (String) token.getCredentials();
UserDetails user = userDetailsService.loadUserByUsername(mobile);
logger.info(code);
//fixme: 驗(yàn)證code
if (code != code) {
throw new CredentialsExpiredException("$code expired.");
}
//返回認(rèn)證完成Token
return new SmsToken(user, null, user.getAuthorities());
}
//支持自定義Token
@Override public boolean supports(Class<?> authentication) {
return SmsToken.class.isAssignableFrom(authentication);
}
整合oauth2 三方認(rèn)證登錄
項(xiàng)目地址:https://github.com/athc/hippo/tree/master/oauth-third
參數(shù)注入到類
@Bean
@ConfigurationProperties("sina.client")
public AuthorizationCodeResourceDetails sina() {
return new AuthorizationCodeResourceDetails();
}
@Bean
@Qualifier("sinaResource")
@Primary
@ConfigurationProperties("sina.resource")
public ResourceServerProperties sinaResource() {
return new ResourceServerProperties();
}
三方登錄請(qǐng)求攔截
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);
//認(rèn)證成功處理
sinaFilter.setAuthenticationSuccessHandler(authSuccessHandler);
//獲取到三方信息 自定義處理 存庫等
sinaTokenServices.setAuthoritiesExtractor(new MyAuthoritiesExtractor());
//三方登錄權(quán)限處理
sinaTokenServices.setPrincipalExtractor(new MyPrincipalExtractor());
filters.add(sinaFilter);
filter.setFilters(filters);
return filter;
}
將攔截器放到攔截鏈中
自定義token:
實(shí)現(xiàn)自定義token產(chǎn)生
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數(shù)據(jù)結(jié)構(gòu)
return result;
}
}
從獲取的三方信息中 獲取有用的信息
MyPrincipalExtractor implements PrincipalExtractor
三方認(rèn)證登錄獲取的權(quán)限
MyAuthoritiesExtractor implements AuthoritiesExtractor
security+oauth2+sso demo地址:https://github.com/athc/ath-cloud -----用的kotlin寫的單點(diǎn)登錄demo
參考鏈接:
https://spring.io/guides/tutorials/spring-boot-oauth2/