I have implemented the JWT Application
I am using JWT 0.9.1 version
I am able to generate the Json web token, but when I pass to the http://localhost:8080/api/v1/greeting method I am always getting the same error like 401 Unauthorized.
SpringSecurityConfig.java
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
#Autowired
private UserDetailsService jwtUserDetailsService;
#Autowired
private JwtRequestFilter jwtRequestFilter;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
// configure AuthenticationManager so that it knows from where to load
// user for matching credentials
// Use BCryptPasswordEncoder
auth.userDetailsService(jwtUserDetailsService).passwordEncoder(passwordEncoder());
}
/*#Override
protected void configure(HttpSecurity http) throws Exception{
http.csrf().disable().authorizeRequests().antMatchers("/").permitAll();
}*/
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
// dont authenticate this particular request
.authorizeRequests().antMatchers("/api/v1/authenticate", "/api/v1/register","/api/v1/basicauth")
/*.permitAll().antMatchers(HttpMethod.OPTIONS, "/**")*/
// all other requests need to be authenticated
.permitAll().anyRequest().authenticated().and().
// make sure we use stateless session; session won't be used to
// store user's state.
exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// Add a filter to validate the tokens with every request
httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
}
GreetingController.java
#CrossOrigin(origins = "http://localhost:4200")
#RestController
#RequestMapping("/api/v1")
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#RequestMapping("/greeting")
public Greeting greeting(#RequestParam(value = "name", defaultValue = "World") String name) {
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
}
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'm trying to setup a simple Oauth2 Server with Spring Boot with only client_credentials flow for now, using in memory users and clients etc. Basically the most basic project so I can build on it. Haven't used Oauth2 with Spring for some time so I'm a bit stuck. I can get access tokens but it seems Spring does not actually validate the username/password sent by the client. Here are the two config classes (AuthorizationServerConfigurerAdapter, WebSecurityConfigurerAdapter):
#Configuration
#EnableAuthorizationServer
public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter {
private final AuthenticationManager authenticationManager;
private final PasswordEncoder passwordEncoder;
private final UserDetailsService userService;
#Value("${jwt.signing-key}")
private String jwtSigningKey;
#Value("${jwt.accessTokenValidititySeconds}")
private int accessTokenValiditySeconds;
#Value("${jwt.refreshTokenValiditySeconds}")
private int refreshTokenValiditySeconds;
public OAuthConfiguration(AuthenticationManager authenticationManager, PasswordEncoder passwordEncoder, UserDetailsService userService) {
this.authenticationManager = authenticationManager;
this.passwordEncoder = passwordEncoder;
this.userService = userService;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("test-client-id")
.secret(passwordEncoder.encode("test-client-secret"))
.accessTokenValiditySeconds(accessTokenValiditySeconds)
.refreshTokenValiditySeconds(refreshTokenValiditySeconds)
.authorizedGrantTypes("client_credentials")
.scopes("read", "write")
.resourceIds("api");
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
.accessTokenConverter(accessTokenConverter())
.userDetailsService(userService)
.authenticationManager(authenticationManager);
}
#Bean
JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(jwtSigningKey);
return converter;
}
}
and:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
public WebSecurityConfiguration(CustomAuthenticationEntryPoint customAuthenticationEntryPoint) {
this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;
}
#Bean
public DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsService(userDetailsService);
return provider;
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
#Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user1")
.password("$2a$10$sWszOXuTlN0amQi8vXp4cerb.tJUQo.4FzLAnTCsSqChsYhlLdQWW")
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().disable().csrf().disable()
.authorizeRequests()
.anyRequest().authenticated()
.and().exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint).accessDeniedHandler(new CustomAccessDeniedHandler());
}
}
So I can call the "/oauth/token" endpoint with any username/password I get an access token. However the client ID and client secret are being validated.
Any help is appreciated
It's the default behaviour with grant_type client_credentials. So, you don't need to pass the username and password to the token endpoint with this grant_type. In this flow, AuthorizationServer will only validate the client Id and client secret.
POST /token
Host: authorization-server.com
grant_type=client_credentials
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx
I try to authenticate throught a /authenticate api to generate a token but it always gives me access denied even when username and password are correct.the problem was int the code of csrf().disabled().
I tried to remove CSRF().disabled() from the configure method it does remove the problem of access denied but it does not give me anything as a response, it does not generate the JWT(nothing is sent as a response).
Here is my controller:
#RestController
#RequestMapping("/api")
public class SecurityController {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private MyUserDetailService userDetailsService;
#Autowired
private JwtUtil jwtUtil;
#GetMapping("/test")
private String test() {
return "Test";
}
#GetMapping("/hello")
private String hello() {
return "hello";
}
#PostMapping("/authenticate")
public ResponseEntity<?> createAuthenticationToken(#RequestBody AuthenticationRequest authenticationRequest)
throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(), authenticationRequest.getPassword()));
} catch (BadCredentialsException e) {
throw new Exception("Incorrect username or password",e);
}
final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
final String jwt = jwtUtil.generateToken(userDetails);
return ResponseEntity.ok(new AuthenticationResponse(jwt));
}
}
securityConfig file:
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private MyUserDetailService myUserDetailService;
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailService);
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable().authorizeRequests().antMatchers("/authenticate").permitAll().anyRequest()
.authenticated();
}
}
your endpoints name is /api/authenticate.
so
.antMatchers("/authenticate")
wont work, you need to match on the correct url.
I`m developing a microservice structure using spring-boot, this structure has an external oauth2 Authorization Server and multiples Resource Servers.
My problem is, each http request to my resources, calls a url to my Authorization Server, to validate the token ( .../oauth/check_token/). This way there is a lot of requests. There is a way to validate/check this token only when it is expired?
My Resource servers:
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Value("${security.oauth2.client.client-id}")
private String clientId;
#Value("${security.oauth2.client.client-secret}")
private String clientSecret;
#Value("${security.oauth2.resource.id}")
private String resourceId;
#Value("${security.oauth2.resource.token-info-uri}")
private String tokenInfoUri;
#Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(ADMIN_ANT_MATCHER).hasRole("ADMIN")
.antMatchers(PROTECTED_ANT_MATCHER).hasRole("USER")
.and()
.csrf().disable();
}
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenService()).resourceId(resourceId).stateless(true);
}
#Bean
#Primary
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl(tokenInfoUri);
tokenService.setClientId(clientId);
tokenService.setClientSecret(clientSecret);
return tokenService;
}
}
Authorization Server:
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Value("${security.oauth2.client.client-id}")
private String clientId;
#Value("${security.oauth2.client.client-secret}")
private String clientSecret;
#Value("${security.oauth2.resource.id}")
private String resourceId;
#Value("${security.oauth2.client.access-token-validity-seconds}")
private Integer tokenValidateSeconds;
#Value("${security.oauth2.client.token-secret}")
private String tokenSecret;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private TokenStore tokenStore;
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private OauthAccessTokenRepository oauthAccessTokenRepository;
#Autowired
private OauthRefreshTokenRepository oauthRefreshTokenRepository;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.tokenStore(this.tokenStore)
.tokenEnhancer(tokenEnhancer())
.authenticationManager(this.authenticationManager)
.userDetailsService(userDetailsService);
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient(clientId)
.authorizedGrantTypes("client_credentials", "password", "refresh_token")
.authorities("ROLE_USER","ROLE_ADMIN")
.scopes("read","write","trust")
.resourceIds(resourceId)
.accessTokenValiditySeconds(tokenValidateSeconds)
.secret(bCryptPasswordEncoder().encode(clientSecret));
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("isAuthenticated()");
}
#Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(this.tokenStore);
return tokenServices;
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new TokenEnhancer() {
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
CustomUser user = (CustomUser) authentication.getPrincipal();
String token = JwtTokenHelper.generateToken(
user.getIdUser(),
user.getTenantID(),
user.getIdEntity(),
user.getIdBusinessUnit(),
user.getProfile(),
tokenValidateSeconds,
tokenSecret
);
((DefaultOAuth2AccessToken) accessToken).setValue(token);
return accessToken;
}
};
}
}
You can remove the need to use the check_token endpoint, by using signed JWT tokens.
When the resource server receive a JWT token, it verify it's signature by using a public key, and the expiration date by checking the corresponding field in the JSON object.
For this you can use the JwtAccessTokenConverter, JwtTokenStore and the nimbus-jose-jwt library.
The downside of this approach is that you cannot revoke a token. It's then preferable to have short lived tokens.
I'm trying to create a server with OAuth 2 but I have a problem. I configured OAuth, the user can authorize and get a token but the REST methods are always accesible, for example a user can use method POST when they didn't authorize.
How to configure OAuth so the REST methods run only when a user did authorize?
This is how some of my code looks like (I used this example code):
OAuthConfiguration class
#Configuration
public class OAuth2ServerConfiguration {
private static final String RESOURCE_ID = "restservice";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends
ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
// #formatter:off
resources
.resourceId(RESOURCE_ID);
// #formatter:on
}
#Override
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.antMatchers("/users").hasRole("ADMIN")
.antMatchers("/greeting").authenticated();
// #formatter:on
}
}
AuthorizationServerConfiguration class:
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
// #formatter:off
endpoints
.tokenStore(this.tokenStore)
.authenticationManager(this.authenticationManager)
.userDetailsService(userDetailsService);
// #formatter:on
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients
.inMemory()
.withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.resourceIds(RESOURCE_ID)
.secret("123456");
// #formatter:on
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(this.tokenStore);
return tokenServices;
}
}
Rest controller:
#RestController
#RequestMapping("/ABC")
final class Controller {
#Autowired
Repository repository;
#RequestMapping(method = RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
int create(#RequestBody #Valid Data myData) {
repository.create(myData);
return 1;
}
#RequestMapping(value = "{number}", method = RequestMethod.GET)
Data findByNumber(#PathVariable("number") String number) {
Data data = repository.findByNumber(number);
return data;
}
#RequestMapping(value = "{number}", method = RequestMethod.PUT)
int update(#RequestBody #Valid Data myData) {
int rows = repository.update(myData);
return 1;
}
#RequestMapping(value = "{number}", method = RequestMethod.DELETE)
int delete(#PathVariable("number") String number) {
repository.delete(serialNumber);
return 1;
}
}
You'll want to add .antMatchers("/ABC/**").authenticated()
See jhipster sample oauth2 example
https://github.com/jhipster/jhipster-sample-app-oauth2/blob/master/src/main/java/com/mycompany/myapp/config/OAuth2ServerConfiguration.java