既然系统重构,那么就从最基础的登录和安全说起!
(摘自:https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-security)
1.security
原文:If Spring Security is on the classpath then web applications will be secure by default with ‘basic’
authentication on all HTTP endpoints.
也就是说类路径下只要有了spring security 的jar包,则之后对系统的请求都会加以验证是否登录过,是否有权限访问。
2.OAuth2
原文:If you have spring-security-oauth2 on your classpath you can take advantage of some auto-
configuration to make it easy to set up Authorization or Resource Server.
有了spring-security-oauth2后则可以配置自己的授权服务器和资源服务器。(授权服务器和资源服务器可以是同一个,也可以是 两个分开)。
2.1Authorization Server
原文:To create an Authorization Server and grant access tokens you need to use
@EnableAuthorizationServer and provide security.oauth2.client.client-id and
security.oauth2.client.client-secret] properties. The client will be registered for you in an
in-memory repository.
关键的一点是启用@EnableAuthorizationServer注解,并配置保存相关的客户端的信息。默认 是将客户端信息存在内存中。在本系统中我们将OAuthClientDetails 信息存在数据库中。
2.2Resource Server
原文:To use the access token you need a Resource Server (which can be the same as the Authorization
Server). Creating a Resource Server is easy, just add @EnableResourceServer and provide some
configuration to allow the server to decode access tokens.
如果应用是一个单独的服务则还需要配置额外的信息:
• security.oauth2.resource.user-info-uri
• security.oauth2.resource.token-info-uri
若是两者同时指定,则token-info-uri 优先级默认高于另一方 (prefer-token-info=true is the default)
3.Client
原文:To make your webapp into an OAuth2 client you can simply add @EnableOAuth2Client
and Spring Boot will create an OAuth2RestTemplate for you to @Autowire.
启用@EnableOAuth2Client注解,并将在授权服务器上注册过的客户端信息配置好,则你的应用变成了客户端。
4.Single Sign On
An OAuth2 Client can be used to fetch user details from the provider (if such features are available)
and then convert them into an Authentication token for Spring Security. The Resource Server
above support this via the user-info-uri property This is the basis for a Single Sign On (SSO)
protocol based on OAuth2, and Spring Boot makes it easy to participate by providing an annotation
@EnableOAuth2Sso.
在步骤3的基础上,springboot 又支持了单点登录。同样是启用注解,启用该注解时@EnableOAuth2Client可以去掉,因为@EnableOAuth2Sso已经包含了该注解。
这样配置出来的资源服务器是一个oauth2里的客户端角色,支持单点登录,而且保护着受保护的资源。
这里只列出资源服务器的相关配置,配置文件和代码片段如下:
单点登录相关配置:
@Component
@EnableOAuth2Sso
@EnableWebSecurity
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
protected static class ConfigurerWebSecurity extends WebSecurityConfigurerAdapter {
@Autowired
private AccessDecisionManager accessDecisionManager;
@Autowired
private SessionRegistry sessionRegistry;
@Autowired
private ObjectPostProcessor<FilterSecurityInterceptor> objectPostProcessorFilterSecurityInterceptor;
@Value("${security.oauth2.authorization.uri}")
String authorizationUri;
/**
* Override this method to configure the {@link HttpSecurity}. Typically subclasses should not invoke this
* method by calling super as it may override their configuration. The default configuration is:
*
* <pre>
* http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
* </pre>
*
* Configure the {@link CookieCsrfTokenRepository} here.
*
* @param http
* the {@link HttpSecurity} to modify
* @throws Exception
* if an error occurs
*/
@Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
// @formatter:off
// TODO CSRF protection
.csrf()
.disable()
// .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// .and()
.authorizeRequests()
.withObjectPostProcessor(objectPostProcessorFilterSecurityInterceptor)
.accessDecisionManager(accessDecisionManager)
// .antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
// session manager
.sessionManagement()
// .sessionFixation().changeSessionId()
// .sessionAuthenticationStrategy(sessionAuthenticationStrategy())
// .maxSessionsPreventsLogin(true)
.maximumSessions(1)
.sessionRegistry(sessionRegistry)
.and()
.and()
// logout
.logout()
.logoutSuccessHandler(logoutSuccessHandler())
.permitAll()
.and()
// remember me
.rememberMe()
.tokenValiditySeconds(7 * 24 * 60 * 60)
// .tokenRepository(tokenRepository());
// .key("hywweb#28936f8ad1b1598f5d877612a7b75163")
.and();
// @formatter:on
;
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
// @formatter:off
String[] urls = {
"/image/**",
"/"
};
// @formatter:on
web.ignoring().antMatchers(urls);
}
@Bean
@Lazy
public SavedRequestAwareAuthenticationSuccessHandler authenticationSuccessHandler() {
SavedRequestAwareAuthenticationSuccessHandler handler = new SavedRequestAwareAuthenticationSuccessHandler();
handler.setTargetUrlParameter("targetUrl");
handler.setDefaultTargetUrl("/security/me");
return handler;
}
@Bean
@Lazy
public LogoutSuccessHandler logoutSuccessHandler() {
SimpleUrlLogoutSuccessHandler handler = new SimpleUrlLogoutSuccessHandler() {
@Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
try {
String targetUrl = super.determineTargetUrl(request, response);
URI uri = new URI(targetUrl);
if (!uri.isAbsolute()) {
uri = new URI(request.getScheme(), request.getRemoteHost(), targetUrl, null);
}
targetUrl = UriUtils.encode(uri.toString(), "utf-8");
return authorizationUri + "/logout?targetUrl=" + targetUrl;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
};
handler.setTargetUrlParameter("targetUrl");
return handler;
}
@Bean
@Lazy
public SecurityAuditorAware auditorProvider(SecurityUserDetailsService userDetailsService) {
return new SecurityAuditorAware(userDetailsService);
}
}
资源服务器相关配置
@Component
@EnableResourceServer
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER)
public class ConfigurerResourceServer extends ResourceServerConfigurerAdapter {
@Autowired
private AccessDecisionManager accessDecisionManager;
@Autowired
private AuthenticationEntryPoint ajaxAuthenticationEntryPoint;
@Autowired
private RequestMatcher ajaxRequestMatcher;
@Autowired
private ResourceServerProperties resourceServerProperties;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
super.configure(resources);
resources.resourceId(resourceServerProperties.getResourceId()).stateless(false);
resources.expressionHandler(expressionHandler);
}
@Override
public void configure(HttpSecurity http) throws Exception {
super.configure(http);
// @formatter:off
String[] urls = {
"/api/**",
"/security/**",
// "/security/me/**",
"/report/**",
"/jms/**",
"/webgis/**"
};
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.requestMatchers()
.antMatchers(urls)
.and()
.authorizeRequests()
.withObjectPostProcessor(objectPostProcessorFilterSecurityInterceptor())
.accessDecisionManager(accessDecisionManager)
.antMatchers("/").permitAll()
.antMatchers(urls).access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))")
.and()
.exceptionHandling()
.defaultAuthenticationEntryPointFor(ajaxAuthenticationEntryPoint, ajaxRequestMatcher)
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
.and()
// @formatter:on
;
}
}
没有了那么多的xml文件,springboot 的autoConfig 确实带来了不一样的开发体验 ,简化了对已有技术的使用。