I am trying to implement Oauth2 with spring boot with the configurations as below
Securty configuration:
#Configuration
#EnableWebSecurity(debug = true)
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider());
auth.userDetailsService(userDetailsService);
}
public AuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
return daoAuthenticationProvider;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(11);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().anonymous().disable().authorizeRequests()
.antMatchers("/oauth2/login","/logout").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/oauth2/login").loginProcessingUrl("/login").permitAll();
}
}
Authorization configuration
#Configuration
#EnableAuthorizationServer
public class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenStore tokenStore;
#Autowired
private ClientDetailsService clientDetailsService;
#Bean
public TokenStore tokenStore(DataSource dataSource){
return new JdbcTokenStore(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
enhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer()));
endpoints.authenticationManager(authenticationManager)
.tokenStore(tokenStore)
.tokenEnhancer(enhancerChain)
.tokenGranter(new CompositeTokenGranter(getCustomizedTokenGranters()))
.tokenServices(tokenServices())
.approvalStoreDisabled();
}
#Bean
#Primary
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenEnhancer(tokenEnhancer());
tokenServices.setTokenStore(tokenStore);
tokenServices.setClientDetailsService(clientDetailsService);
return tokenServices;
}
#Bean
public TokenEnhancer tokenEnhancer(){
return (accessToken, authentication) -> {
if(!"client_credentials".equalsIgnoreCase(authentication.getOAuth2Request().getRequestParameters().get(OAuth2Utils.GRANT_TYPE)))
{
ExtendedUser principal = (ExtendedUser) authentication.getPrincipal();
Map<String, Object> additionalInfo = Maps.newHashMap();
additionalInfo.put("user_id", principal.getUserId());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
}
return accessToken;
};
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("permitAll()")
.tokenKeyAccess("permitAll()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
#Bean
public ClientDetailsService clientDetailsService(DataSource dataSource){
return new CachedClientDetailsService(dataSource);
}
private List<TokenGranter> getCustomizedTokenGranters() {
AuthorizationServerTokenServices tokenServices = tokenServices();
ClientDetailsService clientDetails = clientDetailsService;
OAuth2RequestFactory requestFactory = new DefaultOAuth2RequestFactory(clientDetails);
RefreshTokenGranter refreshTokenGranter = new RefreshTokenGranter(tokenServices, clientDetails, requestFactory);
ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory);
ClientCredentialsTokenGranter clientCredentialsTokenGranter = new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory);
clientCredentialsTokenGranter.setAllowRefresh(true);//custom config, see AuthorizationServerEndpointsConfigurer.getDefaultTokenGranters
List<TokenGranter> tokenGranters = Lists.newArrayList();
tokenGranters.add(refreshTokenGranter);
tokenGranters.add(implicit);
tokenGranters.add(clientCredentialsTokenGranter);
if (authenticationManager != null) {
tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory));
}
return tokenGranters;
}
}
Resource server configuration :
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**").authenticated();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("identity-service");
}
#Bean
public ResourceServerTokenServices resourceServerTokenServices(TokenStore tokenStore){
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore);
return tokenServices;
}
}
application.properties
security.oauth2.resource.filter-order = 3
the resource server is on the same authorization server (Same Application), and I am trying to implement the Implicit grant (Password grant working just fine)
when I am trying to login in to complete the implicit grant (oauth/authorize endpoint need authentication) I am getting /login 404 ?
spring boot: 1.5.10,
spring security Oauth2: 2.0.14
Finally, i managed to make it work
Security Configuration :
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/**").and().csrf().disable().authorizeRequests()
.antMatchers("/oauth2/login","/logout").permitAll()
.antMatchers("/oauth/authorize").authenticated()
.and().formLogin().loginPage("/oauth2/login").loginProcessingUrl("/login").permitAll();
}
Resources Server Configuration
public void configure(HttpSecurity http) throws Exception {
http.requestMatchers().antMatchers("/api/**").and().anonymous().disable().authorizeRequests()
.anyRequest().authenticated();
}
I had to enable the anonymous in spring security and specify mapping URI matcher for both spring security and resource server
Resource server on /api/**
,Spring security on /**
and for taking care of the ordering (on version 1.5.10)
application.properties
security.oauth2.resource.filter-order = 3
Related
I am trying to have a custom authentication provider where I would like to have a keycloak as an authenticator.
Have created just a simple class for authentication provider but its not being called.
#Component
public class CustomKeycloakAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication auth)
throws AuthenticationException {
String username = auth.getName();
String password = auth.getCredentials()
.toString();
if ("externaluser".equals(username) && "pass".equals(password)) {
return new UsernamePasswordAuthenticationToken
(username, password, Collections.emptyList());
} else {
throw new
BadCredentialsException("External system authentication failed");
}
}
#Override
public boolean supports(Class<?> auth) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(auth));
}
}
Here is my WebConfig class:
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
//#EnableMyHttpSession
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientDetailsService;
private AccessDecisionManager accessDecisionManager;
#Autowired
private CustomKeycloakAuthenticationProvider keycloakAuthenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().accessDecisionManager(accessDecisionManager)
// .antMatchers("/oauth/token", "/*.html", "/js/**", "/images/**",
// "/styles/**").permitAll().
.antMatchers("/service/*").fullyAuthenticated()
// .antMatchers("/reports/*").fullyAuthenticated()
.anyRequest().permitAll().and().httpBasic().and().csrf().disable();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(keycloakAuthenticationProvider);
}
#Override
#Bean(name = "authenticationManagerBean")
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
#Bean
public AffirmativeBased accessDecisionManager() {
List<AccessDecisionVoter<?>> accessDecisionVoters = new ArrayList<>();
accessDecisionVoters.add(new ScopeVoter());
accessDecisionVoters.add(new RoleVoter());
accessDecisionVoters.add(new AuthenticatedVoter());
AffirmativeBased accessDecisionManager = new AffirmativeBased(accessDecisionVoters);
return accessDecisionManager;
}
I also have a Authorization Server:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static final Logger log = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);
//private static String REALM="MY_OAUTH_REALM";
#Autowired
private DataSource dateSource;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private CustomClientBuilder customClientBuilder;
#Autowired
private TokenStore tokenStore;
#Autowired
public UserService userService;
#Autowired
private CustomClientService customClientService;
#Autowired
private AccessRightsService accessRightService;
private MyTokenServices tokenServices = null;
#Bean
#Primary
public MyTokenServices tokenServices() {
if(tokenServices != null) return tokenServices;
tokenServices = new MyTokenServices();
tokenServices.setTokenStore(tokenStore);
tokenServices.setClientDetailsService(customClientService);
tokenServices.setAuthenticationManager(authenticationManager);
tokenServices.userService = userService;
tokenServices.accessRightService = accessRightService;
return tokenServices;
}
#Bean
#Primary
public TokenStore tokenStore( #Value("${oauth2Token.store.type}") String tokenStoreType) {
TokenStore tokenStore = null;
log.info("Token store is type " + tokenStoreType);
if(tokenStoreType.toLowerCase().equals("jdbc")){
tokenStore = new JdbcTokenStore(dateSource);
}else {
tokenStore = new InMemoryTokenStore();
}
log.info("Token store is " + tokenStore);
return tokenStore;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.setBuilder(customClientBuilder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore)
.tokenServices(tokenServices())
.userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager).addInterceptor(new HandlerInterceptorAdapter() {
#Override
public boolean preHandle(HttpServletRequest hsr, HttpServletResponse rs, Object o) throws Exception {
rs.setHeader("Access-Control-Allow-Origin", "*");
rs.setHeader("Access-Control-Allow-Methods", "*");
rs.setHeader("Access-Control-Max-Age", "3600");
rs.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
log.info(hsr.getMethod());
if(hsr.getMethod().equals("OPTIONS")){
rs.setStatus(200); // hard fix for options
}
return true;
}
});
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.allowFormAuthenticationForClients();
}
When I try to do a login in the providers there are only 2 of them:
I have tried to put #Autowired to protected void configure(AuthenticationManagerBuilder auth) { but it didn't helped
Don't know if there is something in configuration that I am missing so it doesn't add the 3rd authentication provider.
With Spring Security keyclaock can be configured with a class that derive of KeycloakWebSecurityConfigurerAdapter
#Configuration
#EnableWebSecurity
#KeycloakConfiguration
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter
(https://www.keycloak.org/docs/latest/securing_apps/)
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-security-adapter</artifactId>
<version>20.0.2</version>
</dependency>
I think you don't need to create your own AuthenticationProvider keycloak give you an implementation KeycloakAuthenticationProvider
Update OK (but why ?) :
So I think you don't need to override that method
#Override
protected void configure(AuthenticationManagerBuilder auth)
Create another one like this
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
I'm using Spring 2.3.12.RELEASE with Hoxton.SR11 as cloud version. The dependency I'm using is spring-cloud-starter-oauth2.
I just configure all the oauth server but when I'm trying to do login I get a 401 response. I think that I'm missing something but I cannot fix it.
Here is the code:
AuthoritationServerConfig
#RefreshScope
#Configuration
#EnableAuthorizationServer
public class AuthoritationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
private BCryptPasswordEncoder passwordEncoder;
#Autowired
private AuthenticationManager authenticacionManager;
#Autowired
private InfoAdicionalToken infoAdicionalToken;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient(env.getProperty("config.security.oauth.client.id"))
.secret(passwordEncoder.encode(env.getProperty("config.security.oauth.client.secret")))
.scopes("read", "write").authorizedGrantTypes("password", "refresh_token")
.accessTokenValiditySeconds(3600)
.refreshTokenValiditySeconds(3600);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(infoAdicionalToken, accessTokenConverter()));
endpoints.authenticationManager(this.authenticacionManager)
.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter()).tokenEnhancer(tokenEnhancerChain);
}
#Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();
tokenConverter.setSigningKey(env.getProperty("config.security.oauth.jwt.key"));
return tokenConverter;
}
}
SpringSecurityConfig
#Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService usuarioService;
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(this.usuarioService).passwordEncoder(passwordEncoder());
}
#Override
#Bean
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}
ResourceServerConfig
#RefreshScope
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Value("${config.security.oauth.jwt.key}")
private String jwtKey;
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/api/security/oauth/**").permitAll();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenStore(tokenStore());
}
#Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter tokenConverter = new JwtAccessTokenConverter();
tokenConverter.setSigningKey(jwtKey);
return tokenConverter;
}
Zuul application properties
zuul.routes.security.service-id=servicio-oauth
zuul.routes.security.path=/api/security/**
zuul.routes.security.sensitive-headers=Cookie,Set-Cookie
This is a capture of the request:
Request headers:
Response headers:
Thank you in advance.
I'm trying to get an endpoint not accessible (503 error?) without Authorization: Bearer token header
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/authenticate")
.permitAll()
.antMatchers("/api/admin/**")
.fullyAuthenticated()
.anyRequest().authenticated().and().
exceptionHandling()
.and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests();
httpSecurity.addFilterBefore(jwtRequestFilter,
UsernamePasswordAuthenticationFilter.class);
}
#RestController
#CrossOrigin
#RequestMapping("/api/admin")
public class AdminController {
#RequestMapping("/test")
public String testAdmin() {
return "OK; secret test admin";
}
}
however I can access it just fine
What should I change in my configure method?
EDIT:
#Component
public class JwtRequestFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsServiceImpl userDetailsService;
#Autowired
private JwtUtil jwtUtil;
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
final String authorizationHeader = request.getHeader("Authorization");
String username = null;
String jwt = null;
if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
jwt = authorizationHeader.substring(7);
username = jwtUtil.extractUsername(jwt);
}
if (username != null && SecurityContextHolder
.getContext().getAuthentication() == null) {
UserDetails userDetails = this
.userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(jwt, userDetails)) {
UsernamePasswordAuthenticationToken
usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
usernamePasswordAuthenticationToken
.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
chain.doFilter(request, response);
}
}
It seems that the jwtRequestFilter's doFilterInternal method never runs: I tried setting the breakpoints in the debugger and the execution never stopped there.
EDIT: whole SecurityConfig:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
public SecurityConfig(
UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Bean
DaoAuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider daoAuthenticationProvider =
new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService(this.userDetailsService);
return daoAuthenticationProvider;
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/api/login").permitAll()
// all other requests need to be authenticated
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter,
UsernamePasswordAuthenticationFilter.class);
}
#Bean
BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
HTTP response 503 means service unavailable. You should get 401 Unauthorized when token is missing.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/login").permitAll()
// all other requests need to be authenticated
.anyRequest().authenticated().and()
.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
Using AuthenticationEntryPoint.
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -1L;
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
}
}
I managed to solve it. Turns out the problem was in me not having correct configurations, so the SecurityConfig never even got applied. I fixed it this way:
WebConfig.java:
#Configuration
#ComponentScan("testproject")
#EnableWebMvc
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = "testproject",
entityManagerFactoryRef = "entityManagerFactory", transactionManagerRef = "transactionManager")
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver bean = new InternalResourceViewResolver();
bean.setViewClass(JstlView.class);
bean.setPrefix("/WEB-INF/view/");
bean.setSuffix(".html");
return bean;
}
#Bean
public UserDetailsService userDetailsService() {
UserDetailsService userDetailsService =
new UserDetailsServiceImpl();
return userDetailsService;
}
}
MyAppInitializer.java (notice the commented out sc.addListener(new ContextLoaderListener(root)); line, it must be like that, otherwise there are errors - the fix was suggested to me in another SO question):
public class MyAppInitializer extends
AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void onStartup(final ServletContext sc) throws ServletException {
System.out.println("onStartup!");
AnnotationConfigWebApplicationContext root =
new AnnotationConfigWebApplicationContext();
root.register(WebConfig.class);
root.setServletContext(sc);
root.scan("testproject");
//sc.addListener(new ContextLoaderListener(root));
ServletRegistration.Dynamic appServlet =
sc.addServlet("dispatcher", new DispatcherServlet(new GenericWebApplicationContext()));
appServlet.setLoadOnStartup(1);
appServlet.addMapping("/");
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] {SecurityConfig.class};
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
SecurityWebApplicationInitializer.java:
public class SecurityWebApplicationInitializer extends
AbstractSecurityWebApplicationInitializer {
public SecurityWebApplicationInitializer() {
super(SecurityConfig.class, WebConfig.class);
}
}
I tried to make an oauth server with separate resource server but I can't have a connection between them.
OAuth2Config:
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter
{
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private ResourceLoader resourceLoader;
#Value("${logistics.oauth.tokenTimeout:36000}")
private int expiration;
#Value("${security.jwt.resource-ids}")
private String resourceIds;
#Value("${server.ssl.key-store}")
private String keystore;
#Value("${server.ssl.key-store-password}")
private String password;
#Value("${server.ssl.key-alias}")
private String alias;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
configurer.tokenStore(tokenStore())
.accessTokenConverter(accessTokenConverter())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("logistics").secret(passwordEncoder().encode("secret")).accessTokenValiditySeconds(expiration)
.scopes("read", "write").authorizedGrantTypes("password", "refresh_token","authorization_code").resourceIds(resourceIds);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
}
#Bean
#Primary
public DefaultTokenServices tokenServices(){
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setTokenEnhancer(accessTokenConverter());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
#Bean
public TokenStore tokenStore() {return new JwtTokenStore(accessTokenConverter());}
#Bean
public JwtAccessTokenConverter accessTokenConverter(){
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource resource = resourceLoader.getResource(keystore);
KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(resource,password.toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair(alias));
return converter;
}
#Bean
public TokenEnhancer tokenEnhancer(){return new CustomTokenEnhancer();}
}
WebsecurityConfig OAut2Server:
#Configuration
#EnableWebMvc
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private UserDetailsService userDetailsService;
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
#Override
protected void configure(final HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests().antMatchers("/login").permitAll()
.antMatchers("/oauth/token/revokeById/**").permitAll()
.antMatchers("/tokens/**").permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
// #formatter:on
}
#Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
application.properties:
server.port=81
server.ssl.key-alias=alias
server.ssl.key-store-type=jks
server.ssl.key-store-password=password
server.ssl.key-password=password
server.ssl.key-store=classpath:keystore.jks
server.ssl.enabled=true
server.ssl.ciphers=TLS_RSA_WITH_AES_128_CBC_SHA256,
INCLUDE_ANY_OTHER_ONES_YOU_NEED_TO_SUPPORT
server.ssl.protocol=TLS
server.ssl.enabled-protocols=TLSv1.2
I don't know what I'm doing wrong. I've followed multiple tutorials but none of them worked. Can someone see what I'm doing wrong?
I have a web application with 2 types of resources.
web pages
web services
I want to secure the web pages using one authentication provider (i.e. CAS) and the web services using another authentication provider (i.e. BASIC authentication).
I found a solution which could work here, but it uses XML, and I would prefer to not use XML configuration if possible.
Is there a Java Config solution to this?
Well it took a while to figure out how to do it...
Basically I split up my original security configuration class into 3 separate configuration classes.
This is basically how I did it...
The main security configuration...
#Configuration
#Import({WebPageSecurityConfig.class, WebServiceSecurityConfig.class})
public class SecurityConfig {
}
The security configuration for web pages... (URL does not begin with /service/**)
#Configuration
#Order(200)
#EnableWebMvcSecurity
public class WebPageSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(casAuthenticationProvider());
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http.csrf().disable();
http.requestMatcher(new RequestMatcher() {
#Override
public boolean matches(final HttpServletRequest request) {
final String url = request.getServletPath() + StringUtils.defaultString(request.getPathInfo());
return !(url.startsWith("/service/"));
}
});
http.addFilter(casAuthenticationFilter()).exceptionHandling().authenticationEntryPoint(casAuthenticationEntryPoint());
http.authorizeRequests().
antMatchers("/securedPage").hasAuthority("ROLE_CAS_USER"). // /securedPage can only be accessed by cas user
anyRequest().permitAll(); // all other pages are unsecured
}
// General Application Security (CAS Authentication)
#Bean
public CasAuthenticationFilter casAuthenticationFilter() throws Exception {
final CasAuthenticationFilter casAuthenticationFilter = new CasAuthenticationFilter();
casAuthenticationFilter.setAuthenticationManager(authenticationManager());
return casAuthenticationFilter;
}
#Bean
public CasAuthenticationEntryPoint casAuthenticationEntryPoint() {
final CasAuthenticationEntryPoint casAuthenticationEntryPoint = new CasAuthenticationEntryPoint();
casAuthenticationEntryPoint.setLoginUrl(env.getRequiredProperty("cas.server.url") + "/login");
casAuthenticationEntryPoint.setServiceProperties(casServiceProperties());
return casAuthenticationEntryPoint;
}
#Bean
public ServiceProperties casServiceProperties() {
final ServiceProperties serviceProperties = new ServiceProperties();
serviceProperties.setService(env.getRequiredProperty("cas.service.url") + "/j_spring_cas_security_check");
serviceProperties.setSendRenew(false);
return serviceProperties;
}
#Bean
public CasAuthenticationProvider casAuthenticationProvider() {
final CasAuthenticationProvider casAuthenticationProvider = new CasAuthenticationProvider();
casAuthenticationProvider.setAuthenticationUserDetailsService(casAuthenticationUserDetailsService());
casAuthenticationProvider.setServiceProperties(casServiceProperties());
casAuthenticationProvider.setTicketValidator(casTicketValidator());
casAuthenticationProvider.setKey("casAuthenticationProviderKey");
casAuthenticationProvider.setStatelessTicketCache(casStatelessTicketCache());
return casAuthenticationProvider;
}
#Bean
public AuthenticationUserDetailsService casAuthenticationUserDetailsService() {
final AbstractCasAssertionUserDetailsService authenticationUserDetailsService = new AbstractCasAssertionUserDetailsService() {
#Override
protected UserDetails loadUserDetails(final Assertion assertion) {
final String username = assertion.getPrincipal().getName();
final List<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(new SimpleGrantedAuthority("ROLE_CAS_USER"));
return new User(username, "notused", authorities);
}
};
return authenticationUserDetailsService;
}
#Bean
public TicketValidator casTicketValidator() {
final Saml11TicketValidator ticketValidator = new Saml11TicketValidator(env.getRequiredProperty("cas.server.url"));
ticketValidator.setTolerance(env.getRequiredProperty("cas.ticket.tolerance", Long.class));
return ticketValidator;
}
#Bean
public StatelessTicketCache casStatelessTicketCache() {
final EhCacheBasedTicketCache ticketCache = new EhCacheBasedTicketCache();
ticketCache.setCache(casCache());
return ticketCache;
}
#Bean(initMethod = "initialise", destroyMethod = "dispose")
public Cache casCache() {
final Cache cache = new Cache("casTickets", 50, true, false, 3600, 900);
return cache;
}
#Autowired
private Environment env;
}
The security configuration for RESTful web services (URL starts with /service/**)
#Configuration
#Order(300)
#EnableWebMvcSecurity
public class WebServiceSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().
withUser("admin").password("password").authorities(new SimpleGrantedAuthority("ROLE_WS_USER"));
}
#Override
public void configure(final HttpSecurity http) throws Exception {
http.csrf().disable();
http.
antMatcher("/service/**"). // only process URLs that begin with /service/
sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and(). // RESTful web services are stateless
addFilter(wsAuthenticationFilter()).exceptionHandling().authenticationEntryPoint(wsAuthenticationEntryPoint());
http.authorizeRequests().anyRequest().hasAuthority("ROLE_WS_USER"); // all requests are secured
}
// Web Service Security (BASIC Authentication)
#Bean
public BasicAuthenticationFilter wsAuthenticationFilter() throws Exception {
final BasicAuthenticationFilter wsAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager(), wsAuthenticationEntryPoint());
return wsAuthenticationFilter;
}
#Bean
public BasicAuthenticationEntryPoint wsAuthenticationEntryPoint() {
final BasicAuthenticationEntryPoint wsAuthenticationEntryPoint = new BasicAuthenticationEntryPoint();
wsAuthenticationEntryPoint.setRealmName("My Realm");
return wsAuthenticationEntryPoint;
}
#Autowired
private Environment env;
}
It's explain how to create multiple securities in the docs
http://docs.spring.io/spring-security/site/docs/current/reference/html/jc.html#multiple-httpsecurity
something like this should work
#EnableWebSecurity
public class MultiHttpSecurityConfig {
#Configuration
#Order(1)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Resource private UserDetailsService userBasicAuthService;
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/api/**")
.userDetailsService(userBasicAuthService)
.authorizeRequests()
.and()
.httpBasic();
}
}
#Configuration
public static class PagesWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Resource private UserDetailsService userCasService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/pages/**")
.userDetailsService(userCasService)
.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin();
}
}
}