I'm attempting to create a site. The pages that are available to anyone are the landing page, the login page, the register page, the about us page, and the contact page as seen below.
#Configuration
public class CustomWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
private final AuthenticationManager authenticationManager;
#Autowired
public CustomWebSecurityConfigurerAdapter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManager);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/join", "/login", "/about", "/contact").permitAll()
.and().authorizeRequests()
.anyRequest().authenticated()
.and().exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
.and()
.logout().logoutSuccessUrl("/").permitAll()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
}
Here is my OAuth2 config:
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("my-client")
.authorizedGrantTypes("client_credentials", "authorization_code")
.authorities("ROLE_CLIENT")
.scopes("read", "write")
.resourceIds("business-resource")
.secret("secret");
}
}
And my controller:
#EnableResourceServer
#RestController
public class BusinessAccountController {
private static final Logger logger = Logger.getLogger(BusinessAccountController.class);
#Autowired
BusinessUserService businessUserService;
/*URL mapping constants for each particular controller method*/
private static final String LOGIN_URL_MAPPING = "/login";
private static final String USER_ENDPOINT_URL_MAPPING = "/user";
private static final String ACCOUNT_CREATION_URL_MAPPING = "/join";
/**
* #param principal
* #return
*/
#RequestMapping(USER_ENDPOINT_URL_MAPPING)
public Principal user(Principal principal) {
return principal;
}
#RequestMapping(value = LOGIN_URL_MAPPING, method = RequestMethod.POST)
public ResponseEntity<BusinessUser> login(#RequestBody BusinessUser inputUser) {
BusinessUser requestedUser = businessUserService.findByUsername(inputUser.getUsername());
if(requestedUser != null)
if(BCrypt.checkpw(inputUser.getPassword(), requestedUser.getPassword()))
return new ResponseEntity<>(requestedUser, HttpStatus.OK);
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
...
For some reason, every single page requires an authentication token(gives a 401 unauthorized response), even the ones I specified permitAll() on (login, contact, about us, etc...). Why is Spring asking for an authentication token for the pages I specified to be accessed without authentication?
Side note: If I generate an access token, I'm able to register, log in, and access unauthorized pages. I want to be able to access the specified pages in my WebSecurityConfigurerAdapter without an authentication token.
Related
I am trying to secure my api requests with both basic authorization and jwt role based authorization.
I have two classes for basic auth and web security config. Both jwt role based auth and basic auth classed are imported in WebSecurityConfigurerAdapter.
When running the application, api is working only with basic auth and does not know jwt token included or not.
WebSecurityConfigurerAdapter class
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
final private static String REALM = "UWAPP_SECURITY_REALM";
#Autowired
UserDetailsServiceImpl userDetailsService;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Autowired
public WebSecurityConfig(UserDetailsServiceImpl userDetailsService) {
super();
this.userDetailsService = userDetailsService;
}
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.authorizeRequests()
.antMatchers("/actuator/health", "/api/auth/signup", "/api/auth/login", "/api/auth/logout").permitAll()
.antMatchers("/api/test/public").permitAll()
.antMatchers("/api/test/user").hasAnyAuthority(UserLoginRole.USER.value())
.anyRequest().authenticated()
.and()
.formLogin().disable()
.csrf().disable()
.httpBasic().realmName(REALM)
.authenticationEntryPoint(getBasicAuthEntryPoint());
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
http.cors();
}
#Bean
public BasicAuthentication getBasicAuthEntryPoint() {
return new BasicAuthentication();
}
}
BasicAuthentication class
public class BasicAuthentication extends BasicAuthenticationEntryPoint {
final private static String REALM = "UWAPP_SECURITY_REALM";
#Override
public void commence(final HttpServletRequest request, final HttpServletResponse response,
final AuthenticationException authException) throws IOException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName() + "");
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 : " + authException.getMessage());
}
#Override
public void afterPropertiesSet() {
setRealmName(REALM);
super.afterPropertiesSet();
}
}
Requests did not pass through authorization jwt token included or not.
What am I missing here?
I tried to make an Oauth2 server. It works fine, until I turn on form login. When I do, it redirects me to the login form, even though my request contains the access token.
Authorization server:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("isAnonymous() || hasRole('ROLE_TRUSTED_CLIENT')").checkTokenAccess("hasRole('ROLE_TRUSTED_CLIENT')");
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory().withClient("my-trusted-client")
.authorizedGrantTypes("client_credentials,", "password", "authorization_code")
.authorities("USER")
.scopes("read", "write", "trust")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(3600)
.secret("secret")
.autoApprove(true);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
}
WebSecurity:
#Configuration
public class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.formLogin()
.and()
.httpBasic().disable()
.anonymous().disable()
.authorizeRequests().anyRequest().authenticated();
}
}
Resource server:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/me").authorizeRequests().anyRequest().authenticated();
}
}
User info:
#RestController
public class UserController {
#RequestMapping(value ="/me", produces = "application/json")
public ResponseEntity<Map<String, String>> user(Principal principal) {
HashMap<String, String> result = new HashMap<>();
result.put("username", principal.getName());
return ResponseEntity.ok(result);
}
}
The app:
#SpringBootApplication
#EnableAuthorizationServer
public class Security1Application {
public static void main(String[] args) {
SpringApplication.run(Security1Application.class, args);
}
#Autowired
public void authenticationManager(AuthenticationManagerBuilder authenticationManagerBuilder, UserService userService) throws Exception {
authenticationManagerBuilder.userDetailsService(userService);
}
}
I also have a user service that returns UserDetails.
When I try it from browser, it works fine. I can login and then acces the restricted /me endpoint. But if I get an access token from the token endpoint and try to use it to access /me I get redirected to the login form. If I remove the .formLogin(), then the token works, but on the website I have to login in a popup alert window, which is not very nice.
Hi to all Spring Experts!
I have an issue that I'm trying to solve for a while but i think that i have reached a dead end.
So basically what I need, is to configure my Spring-Security (in Spring-Boot) to have two authentication mechanisms (one for Legacy JSP pages and one for REST APIs). So I followed the following post:
multiple authentication mechanisms in a single app using java config
It worked fine with one LDAP authentication provider. But then I tried to extend my LDAP connection to also obtain a ticket from a third party service (that will be used for future connections to other services), and there I had a problem.
So I created a new Authentication Token, Filter and Authentication provider, but the default UsernamePasswordAuthenticationFilter is being fired first, no matter what I do.
I tried to follow this post How to configure a custom filter programatically in Spring Security? and saw that the problem might be in the fact that my filter was extending UsernamePasswordAuthenticationFilter. So I removed this and tried to have a simple AbstractAuthenticationProcessingFilter, still - no luck.
I think the problem is in my WebSecurity configuration. Currently, with the following code I'm gonna share, the REST Api authentication is returning 405 - method not allowed and the legacy Login is stuck in an infinite loop and crashes, even before I hit "Login".
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true) //Enables #PreAuthorize on methods
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LDAPConfigurationBean ldapBean;
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//HERE GOES LDAP CONNECTION STUFF
// Add the custom LDAP + Token provider to the Authentication provider chain
auth.authenticationProvider(new TicketAndLDAPAuthenticationProvider(authenticator,authoritiesPopulator));
// Creating an LDAP provider using the authenticator and the populator.
auth.authenticationProvider(new LdapAuthenticationProvider(authenticator,authoritiesPopulator));
}
#Configuration
#Order(1)
public static class ConfigureFilters extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.addFilterBefore(new TicketAndLDAPAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
}
}
//Management Endpoints Authorization
#Configuration
#Order(2)
public static class EndpointsWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/manage/health")
.authorizeRequests()
.anyRequest().permitAll();
}
}
//API Authentication+Authorization
#Configuration
#Order(3)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RestAuthSuccessHandler authSuccessHandler;
#Autowired
private RestAuthFailureHandler authFailureHandler;
#Autowired
private RestLogoutSuccessHandler logoutSuccessHandler;
private String LOGIN_PATH = "/api/authenticate";
private String USERNAME = "username";
private String PASSWORD = "password";
protected void configure(HttpSecurity http) throws Exception {
/*CSRF configuration*/
http.csrf().disable();
http
.antMatcher(LOGIN_PATH)
.authorizeRequests()
.anyRequest().permitAll();
http
.antMatcher("/api/**")
//Stateless session creation - no session will be created or used by Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.formLogin().permitAll()
.loginProcessingUrl(LOGIN_PATH)
.usernameParameter(USERNAME)
.passwordParameter(PASSWORD)
.successHandler(authSuccessHandler)
.failureHandler(authFailureHandler)
.and()
.logout().permitAll()
.logoutSuccessHandler(logoutSuccessHandler);
http
.authorizeRequests().anyRequest().authenticated();
}
}
//JSP Authentication+Authorization
#Configuration
#Order(4)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
/*CSRF configuration*/
http.csrf().disable();
/*Static content*/
http
.authorizeRequests()
.antMatchers("/css*//**").permitAll()
.antMatchers("/images*//**").permitAll()
.antMatchers("/scripts*//**").permitAll()
.antMatchers("/fonts*//**").permitAll()
.antMatchers("/login*").anonymous();
/*Login / Logout configuration*/
http
.formLogin()
.loginPage("/login.htm").permitAll()
.defaultSuccessUrl("/index.htm?name=******")
.failureUrl("/login.htm?error=true")
.and()
.logout().permitAll()
.logoutSuccessUrl("/login.htm")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
/*URL roles authorizations*/
http
.authorizeRequests().anyRequest().authenticated();
}
}
}
As you can see, I am trying to configure my filter in the "Configure Filters" method - but I have also tried to configure it inside the adapters, with / without a #Bean annotation - all with no luck.
Filter:
public class TicketAndLDAPAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public TicketAndLDAPAuthenticationFilter() {
super("/*");
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//Save the password for later
String username = request.getParameter("username");
String password = request.getParameter("password");
TicketAndLDAPAuthenticationToken token = new TicketAndLDAPAuthenticationToken(username,password,null);
return token;
}
}
Edit: forgot to add to the filter:
if ( request.getParameter( "username" ) == null || request.getParameter( "password" ) == null ) == null ) {
return null;
}
Now I get 405 in both login mechanisms.
Token:
public class TicketAndLDAPAuthenticationToken extends UsernamePasswordAuthenticationToken {
private AuthTicket otp;
private String restoredPassword;
public TicketAndLDAPAuthenticationToken( String username, String password, RestAuthLoginTicket otp ) {
super( username, password );
this.otp = otp;
}
public AuthTicket getOTP() {
return otp;
}
public AuthTicket getOtp() {
return otp;
}
public void setOtp(AuthTicket otp) {
this.otp = otp;
}
}
Provider:
public class TicketAndLDAPAuthenticationProvider extends LdapAuthenticationProvider {
#Autowired
TokenUtils tokenUtils;
public TicketAndLDAPAuthenticationProvider(LdapAuthenticator authenticator, LdapAuthoritiesPopulator authoritiesPopulator) {
super(authenticator, authoritiesPopulator);
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
TicketAndLDAPAuthenticationToken token = (TicketAndLDAPAuthenticationToken) super.authenticate(authentication);
token.setOtp(tokenUtils.getTicket(token));
return token;
}
#Override
public boolean supports(Class<?> authentication) {
return TicketAndLDAPAuthenticationToken.class.isAssignableFrom(authentication);
}
}
Thanks in advance!!
So I found the issue(s).
First of all, the right way to configure the authentication managers is not how I configured above because there is no antMatcher and this caused my resources and pages to be open to everybody.
Secondly, the problem that caused the infinite redirects and error 405 was that I haven't defined my filter to accept post.
After fixing that, my JSP login form and authentication mechanism worked fine, but the "/api" was redirecting to the login page instead of the resource.
What brings me to my final point - the http.formLogin() is creating a UsernamePasswordAuthenticationFilter. I have two of them - one for each login. So I had to add http.addFilterBefore() for each one of the logins, but with a different URL.
The "/api" url was again using the default redirects of Spring instead of what I have defined, so I had to override them.
These are the configurations and filters that are working for me:
Security Configuration:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true) //Enables #PreAuthorize on methods
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private LDAPConfigurationBean ldapBean;
#Autowired
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
//LDAP Stuff
TicketAndLDAPAuthenticationProvider ticketAndLDAPAuthenticationProvider = new TicketAndLDAPAuthenticationProvider(authenticator,authoritiesPopulator);
auth.authenticationProvider(ticketAndLDAPAuthenticationProvider);
LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(authenticator,authoritiesPopulator);
auth.authenticationProvider(ldapAuthenticationProvider);
}
//Management Endpoints Authorization
#Configuration
#Order(1)
public static class EndpointsWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/manage/health")
.authorizeRequests()
.anyRequest().permitAll();
}
}
//API Authentication+Authorization
#Configuration
#Order(2)
public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private RestAuthenticationEntryPoint authenticationEntryPoint;
#Autowired
private RestAuthSuccessHandler authSuccessHandler;
#Autowired
private RestAuthFailureHandler authFailureHandler;
#Autowired
private RestLogoutSuccessHandler logoutSuccessHandler;
private String LOGIN_PATH = "/api/authenticate";
protected void configure(HttpSecurity http) throws Exception {
/*CSRF configuration*/
http.csrf().disable();
http.addFilterBefore(new TicketAndLDAPAuthenticationFilter(LOGIN_PATH,authSuccessHandler,authFailureHandler), UsernamePasswordAuthenticationFilter.class);
http
.antMatcher("/api/**")
// Stateless session creation - no session will be created or used by Spring Security
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.logout().permitAll()
.logoutSuccessHandler(logoutSuccessHandler);
http
.authorizeRequests().anyRequest().authenticated();
}
}
//JSP Authentication+Authorization
#Configuration
#Order(3)
public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
private String LOGIN_PATH = "/login.htm";
#Override
protected void configure(HttpSecurity http) throws Exception {
/*CSRF configuration*/
http.csrf().disable();
http.addFilterBefore(new TicketAndLDAPAuthenticationFilter(LOGIN_PATH), UsernamePasswordAuthenticationFilter.class);
/*Static content*/
http
.authorizeRequests()
.antMatchers("/css*//**").permitAll()
.antMatchers("/images*//**").permitAll()
.antMatchers("/scripts*//**").permitAll()
.antMatchers("/fonts*//**").permitAll()
.antMatchers("/login*").anonymous();
/*Login / Logout configuration*/
http
.formLogin()
.loginPage(LOGIN_PATH).permitAll()
.defaultSuccessUrl("/index.htm?name=******")
.failureUrl("/login.htm?error=true")
.and()
.logout().permitAll()
.logoutSuccessUrl("/login.htm")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID");
/*URL roles authorizations*/
http
.authorizeRequests().anyRequest().authenticated();
}
}
}
And the Filter:
public class TicketAndLDAPAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public TicketAndLDAPAuthenticationFilter(String defaultProcessUrl) {
super(new AntPathRequestMatcher(defaultProcessUrl, "POST"));
}
public TicketAndLDAPAuthenticationFilter(String defaultProcessUrl, AuthenticationSuccessHandler authenticationSuccessHandler, AuthenticationFailureHandler authenticationFailureHandler) {
super(new AntPathRequestMatcher(defaultProcessUrl, "POST"));
setAuthenticationFailureHandler(authenticationFailureHandler);
setAuthenticationSuccessHandler(authenticationSuccessHandler);
}
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
//Save the password for later
String username = request.getParameter("username");
String password = request.getParameter("password");
if ( username==null || password==null) {
return null;
}
TicketAndLDAPAuthenticationToken token = new TicketAndLDAPAuthenticationToken(username,password,null);
return token;
}
}
I have the following Sprring web app:
#Secured({"ROLE_ADMIN"})
#RequestMapping(value = "data/{id}", method = RequestMethod.GET)
public Object getData(#RequestPath String id)
#RequestMapping(value = "login", method = RequestMethod.GET)
public Object login(#RequestParam String username, #RequestParam String password)
In login I need to call another server, pass credentials and get back roles, then let spring know to use these roles for incoming user.
After login client can use getData method if pass authorization of ROLE_ADMIN.
How can I implement this behaviour using java config?
UPDATE:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public AuthenticationProvider authenticationProvider;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
;
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private static final Logger logger = LogFactory.getLogger();
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String name = authentication.getName();
String password = authentication.getCredentials().toString();
log.debug("name=" + name + " password=" + password);
List<GrantedAuthority> grantedAuths = new ArrayList<>();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
Authentication auth = new UsernamePasswordAuthenticationToken(name, password, grantedAuths);
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
logger.debug("supports authentication=" + authentication);
return true;
}
}
public class SecurityInitializer extends AbstractSecurityWebApplicationInitializer {
}
But as I can see from the logs CustomAuthenticationProvider.authenticate is never called.
Did I miss something?
Thanks.
UPDATE 2: correct solution for me:
Remove Login url from authentication config
add exception handler to disable redirection in case of authentication error
add success handler to send user valid json response
use http POST for app/login
#EnableGlobalMethodSecurity(securedEnabled = true) in web config in order to allow #Secured annotation in controller.
Thanks for all prompts.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
**.anyRequest().authenticated()**
.and().formLogin()
.loginProcessingUrl("/login").usernameParameter("username")
.passwordParameter("password")
**.successHandler(authenticationSuccessHandler)**.failureHandler(authenticationFailureHandler)
.and().csrf().disable().**exceptionHandling()
.authenticationEntryPoint(errorsAuthenticationEntryPoint)**;
}
You need to use WebSecurityConfigurerAdapter like this:
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.logout()
.logoutUrl("/myurl/logout")
.and()
.formLogin()
.loginPage("/myurl/login")
.defaultSuccessUrl("/myurl/login?success");
}
}
Every thing is explain in the documentation https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc-form
You will need to implement a custom AuthenticationProvider. Something like:
#Configuration
#EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void registerGlobalAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider());
}
#Bean
AuthenticationProvider customAuthenticationProvider() {
CustomAuthenticationProvider impl = new CustomAuthenticationProvider();
impl.setUserDetailsService(customUserDetailsService());
/* other properties etc */
return impl ;
}
#Bean
UserDetailsService customUserDetailsService() {
/* custom UserDetailsService code here */
}
}
Working on implementing Oauth2 with Spring. I want to implement the implicit workflow:
My configuration file:
#Configuration
#EnableAutoConfiguration
#RestController
public class App {
#Autowired
private DataSource dataSource;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#RequestMapping("/")
public String home() {
return "Hello World";
}
#Configuration
#EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.tokenStore(tokenStore);
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests().antMatchers("/oauth/token").authenticated()
.and()
.authorizeRequests().anyRequest().permitAll()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.csrf().disable();
}
}
#Configuration
#EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager auth;
private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
#Bean
public JdbcTokenStore tokenStore() {
return new JdbcTokenStore(DBConnector.dataSource);
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(DBConnector.dataSource);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security)
throws Exception {
security.passwordEncoder(passwordEncoder);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints.authorizationCodeServices(authorizationCodeServices())
.authenticationManager(auth).tokenStore(tokenStore())
.approvalStoreDisabled();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.jdbc(DBConnector.dataSource)
.passwordEncoder(passwordEncoder)
.withClient("my-trusted-client")
.secret("test")
.authorizedGrantTypes("password", "authorization_code",
"refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.resourceIds("oauth2-resource")
.accessTokenValiditySeconds(0);
// #formatter:on
}
}
#Autowired
public void init(AuthenticationManagerBuilder auth) throws Exception {
// #formatter:off
auth.jdbcAuthentication().dataSource(DBConnector.dataSource).withUser("dave")
.password("secret").roles("USER");
// #formatter:on
}
}
This is working so far. A user is also generated in the database.
Problem is following. When i try to do following request:
http://localhost:8080/oauth/token?grant_type=authorization_code&client_id=my-trusted-client&username=dave&password=secret
I always get a popup window (authentication) asking me to enter a username and a password. But it doesn't matter what i enter there i never pass through. So what is wrong there?
I would like to have it, that when i call this url, that i get back my access_token.
In case of implicit flow all token will be generated through authorization url instead of token url. so you should hit ../oauth/authorize endpoint with implicit response type. i.e
../oauth/authorize?response_type=implicit&client_id=trusted_client&redirect_uri=<redirect-uri-of-client-application>.
You are getting the username password popup because token endpoint is already protected through spring's BasicAuthenticationFilter and it is expecting you to pass your client_id as username and client_secret as password. Instead of token endpoint you need to protect authorization endpoint so do your endpoint security configuration as given...
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http.authorizeRequests().antMatchers("/oauth/authorize").authenticated()
.and()
.authorizeRequests().anyRequest().permitAll()
.and()
.formLogin().loginPage("/login").permitAll()
.and()
.csrf().disable();
}