I want the following:
The authentication will be done with our GatewayService. It calls the UserService, which checks if the credentials matches. It works for the first and second call (where I don't really know why it works). I need to have an x-auth-token for my frontend, that I can use for authentication after the login.
How do I have to change my CustomAuthProvider or SecurityConfig, so that the authentication is based on the x-auth-token?
My guess is that a filter creates it and authenticates the user with the sent x-auth-token
public class CustomAuthProvider implements AuthenticationProvider {
private final UserAuthService userAuthService;
#Override
public Authentication authenticate( Authentication authentication ) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
User user = userAuthService.authenticate( username, password ); //if exception is thrown, dont authenticate
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken( user.getId(), password, authentication.getAuthorities() );
return token;
}
#Override
public boolean supports( Class<?> authentication ) {
return authentication.equals( UsernamePasswordAuthenticationToken.class );
}
}
My SecurityConfig
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final SecurityConfigSupport securityConfigSupport;
private final CustomAuthProvider customAuthProvider;
private static final String SECRET = "...";
#Autowired
public void configureGlobal( final AuthenticationManagerBuilder auth ) {
auth.authenticationProvider( customAuthProvider );
}
#Override
protected void configure( final HttpSecurity http ) throws Exception {
final TokenAuthenticationService tokenAuthService = new TokenAuthenticationService( SECRET );
final StatelessLoginResponseFilter statelessLoginResponseFilter =
new StatelessLoginResponseFilter( tokenAuthService, authenticationManager() );
http.csrf().disable().cors();
http.addFilter( statelessLoginResponseFilter );
http.addFilter( new StatelessAuthenticationFilter( tokenAuthService ) );
http.sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS );
http.authorizeRequests().anyRequest().authenticated().and().httpBasic().and().formLogin().loginPage( "/api/v1/login" );
}
}
Related
I've created backend for my mobile application with REST API and JWT authentication/authorization.
Then I created android application using Retrofit.
After retrieving JWToken from /login endpoint I've created GET request on a server-side to parse username of currently logged user with token, and then I call method on client-side.
UserController.java (server-side)
#RestController
#RequestMapping(path = "/user")
public class UserController {
private UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
#GetMapping
public String getCurrentUser(#AuthenticationPrincipal Object user) {
user = SecurityContextHolder.getContext().getAuthentication()
.getPrincipal();
return user.toString();
}
}
But I'm not sure if it's a right way to develop it like this.
Let's say I have two tables in my database.
One with login credentials that are being used in Authentication
Second with users personal data
and now I want to display First name and last name of a user.
Now, the only information after login I have is users username that he logged with and if I want to get more information I have to somehow make Queries on client side to:
first - get id of a user where username = username that I got from token
then - get object of users_data where user_id = id from the first query
and I don't think this process should be done on the client side(correct me if I'm wrong, please).
Question
So my question is what should I do fulfill this scenario where I want to get all information about user where I have only his username in client-side app. Should I make changes in my backend, or stick to making queries from mobile app?
(Server-side)
AuthenticationFilter.java
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private AuthenticationManager authenticationManager;
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
#Override
public Authentication attemptAuthentication(
HttpServletRequest request,
HttpServletResponse response
) throws AuthenticationException {
// Mapping credentials to loginviewmodel
LoginViewModel credentials = null;
try {
credentials = new ObjectMapper().readValue(request.getInputStream(), LoginViewModel.class);
} catch (IOException e) {
e.printStackTrace();
}
// Creating login token
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
credentials.getUsername(),
credentials.getPassword(),
new ArrayList<>()
);
// Authenticate user
Authentication auth = authenticationManager.authenticate(authenticationToken);
return auth;
}
#Override
protected void successfulAuthentication(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult
) throws IOException, ServletException {
// Grab current user
UserImpl principal = (UserImpl) authResult.getPrincipal();
// Create JWT Token
String token = JWT.create()
.withSubject(principal.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + JwtProperties.EXPIRATION_TIME))
.sign(Algorithm.HMAC512(JwtProperties.SECRET.getBytes()));
// Add token in response(this is syntax of token)
response.addHeader(JwtProperties.HEADER_STRING, JwtProperties.TOKEN_PREFIX + token);
}
}
AuthorizationFilter.java
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private UserRepository userRepository;
public JwtAuthorizationFilter(
AuthenticationManager authenticationManager,
UserRepository userRepository
) {
super(authenticationManager);
this.userRepository = userRepository;
}
#Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain
) throws IOException, ServletException {
// Read authorization header with JWT Token
String header = request.getHeader(JwtProperties.HEADER_STRING);
if (header == null || !header.startsWith(JwtProperties.TOKEN_PREFIX)) {
chain.doFilter(request, response);
}
// Try get user data from DB to authorize
Authentication authentication = getUsernamePasswordAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
}
private Authentication getUsernamePasswordAuthentication(HttpServletRequest request) {
String token = request.getHeader(JwtProperties.HEADER_STRING);
if (token != null) {
// parse and validate token
String username = JWT.require(Algorithm.HMAC512(JwtProperties.SECRET.getBytes()))
.build()
.verify(token.replace(JwtProperties.TOKEN_PREFIX, ""))
.getSubject();
if (username != null) {
User user = userRepository.findByUsername(username);
UserImpl principal = new UserImpl(user);
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, null, principal.getAuthorities());
return auth;
}
return null;
}
return null;
}
}
UserImpl.java
public class UserImpl implements UserDetails {
private User user;
public UserImpl(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> authorities = new ArrayList<>();
// Get list of roles (ROLE_name)
this.user.getRoleList().forEach( role -> {
GrantedAuthority authority = new SimpleGrantedAuthority("ROLE_" + role);
authorities.add(authority);
});
return authorities;
}
#Override
public String getPassword() {
return this.user.getPassword();
}
#Override
public String getUsername() {
return this.user.getUsername();
}
}
(Client-side)
Method for parsing username from currently logged in User:
public void getCurrentUser() {
Call<String> call = ApiClient.getUserService(getApplicationContext()).getCurrentUser();
call.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
String user = response.body();
nameOfUserView.setText(user);
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
nameOfUserView.setText(t.getMessage());
}
});
}
There is a loophole in your logic. Lets see
#1 - It is fine. Based on JWT Token, you are fetching username
#2 - Using token in header, you are fetching other details by sending username.
In #2, what if I send username of any other user instead of logged in user. System will still respond with the details. So, any logged in user will be able to see details of any user.
To handle this, you should use some DTO class which has all required fields say UserReturnData. Structure will be
public class UserReturnData
{
String username;
List<String> roles;
Long id;
//more fields as per requirement
}
Then in your current user call, populate this data based on authorisation header. Do not send any username. Authorisation header should be sufficient to fetch user details. Sample:
public UserReturnData fetchUserDetails()
{
UserReturnData userReturnData = new UserReturnData();
List<String> roles = new ArrayList<String>();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
userReturnData.setUsername(auth.getName());
Long id = uRepo.findId(auth.getName());
userReturnData.setId(id);
Collection<SimpleGrantedAuthority> authorities = (Collection<SimpleGrantedAuthority>) SecurityContextHolder
.getContext().getAuthentication().getAuthorities();
for (SimpleGrantedAuthority authority : authorities)
{
roles.add(authority.getAuthority());
}
userReturnData.setRoles(roles);
//Populate other required fields here.
return userReturnData;
}
Whenever you need details of logged-in user. You can make call to current user API with only Authorization token and get logged in user information
I am developing a microservices web application.
I have this microservices:
frontEnd service (all html files)
Account service (with Spring Security)
ZuulGateway service
Demo service (a simple rest controller that return a string)
EurekaServer service
My account service works. The login phase works and the JWT creation works. I save it in the header of the response:
response.addHeader("Authorization", "Bearer " + token);
This is the Ajax code in the frontEnd:
/* sign in submit function */
$("#submit").click(function(e) {
e.preventDefault();
$.ajax({ /* Ajax call to AccountMicroservice for login */
url : 'http://localhost:8762/token/generate-token',
type : "POST",
data : {
username : $("#username").val(),
password : $("#password").val()
},
success : function(data) {
console.log(data.token);
window.location.href = 'http://localhost:8762/test';
},
error : function(result) {
alert("Sign in failed!");
console.log(result);
}
});
});
The problems arose with the addition of Spring Cloud Netflix Zuul. The gateway server port is 8762.
Each request to 'http://localhost:8762/token/generate-token' return a 401 error.
These are the Zuul gateway service. I omitted the import.
application.properties:
server.port=8762
spring.application.name=gateway-service
eureka.client.serviceUrl.defaultZone=${EUREKA_SERVER_URL:http://localhost:8761/eureka}
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
zuul.routes.test-service.path=/test/**
zuul.routes.test-service.service-id=test-service
zuul.routes.account-service.path=/token/**
zuul.routes.account-service.service-id=account-service
zuul.routes.auth-service.sensitive-headers=Cookie,Set-Cookie
webSecurityConfig:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler)
.and()
.addFilterAfter(new JwtTokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
// allow all who are accessing "auth" service
.antMatchers(HttpMethod.POST, "/token/**", "/signup").permitAll()
// Any other request must be authenticated
.anyRequest().authenticated();
}
//The CORS filter bean - Configures allowed CORS any (source) to any
//(api route and method) endpoint
#Bean
CorsConfigurationSource corsConfigurationSource() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin(CorsConfiguration.ALL);
//config.addAllowedHeaders(Collections.singletonList(CorsConfiguration.ALL));
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("HEAD");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("PATCH");
source.registerCorsConfiguration("/**", config);
return source;
}
}
JwtTokenUtil:
#Component
public class JwtTokenUtil implements Serializable {
public String getUsernameFromToken(String token) {
return getClaimFromToken(token, Claims::getSubject);
}
public Date getExpirationDateFromToken(String token) {
return getClaimFromToken(token, Claims::getExpiration);
}
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
final Claims claims = getAllClaimsFromToken(token);
return claimsResolver.apply(claims);
}
private Claims getAllClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(SIGNING_KEY)
.parseClaimsJws(token)
.getBody();
}
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
public String generateToken(Authentication authentication) {
final String authorities = authentication.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.joining(","));
return Jwts.builder()
.setSubject(authentication.getName())
.claim(AUTHORITIES_KEY, authorities)
.signWith(SignatureAlgorithm.HS256, SIGNING_KEY)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_VALIDITY_SECONDS*1000))
.compact();
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
return (
username.equals(userDetails.getUsername())
&& !isTokenExpired(token));
}
UsernamePasswordAuthenticationToken getAuthentication(final String token, final Authentication existingAuth, final UserDetails userDetails) {
final JwtParser jwtParser = Jwts.parser().setSigningKey(SIGNING_KEY);
final Jws<Claims> claimsJws = jwtParser.parseClaimsJws(token);
final Claims claims = claimsJws.getBody();
final Collection<? extends GrantedAuthority> authorities =
Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return new UsernamePasswordAuthenticationToken(userDetails, "", authorities);
}
}
JwtTokenAuthenticationFilter:
public class JwtTokenAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private JwtTokenUtil jwtTokenUtil;
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
// 1. get the authentication header. Tokens are supposed to be passed in the authentication header
String header = request.getHeader(HEADER_STRING);
String username = null;
String authToken = null;
// 2. validate the header and check the prefix
if (header != null && header.startsWith(TOKEN_PREFIX)) {
// 3. Get the token
authToken = header.replace(TOKEN_PREFIX,"");
try {
username = jwtTokenUtil.getUsernameFromToken(authToken);
System.out.println("\n\n\n\n\n\n\n\n\n\n" + username + "\n\n\n\n\n\n\n\n\n\n");
} catch (IllegalArgumentException e) {
logger.error("c'รจ stato un errore durante il reperimento dello username dal token", e);
}
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
// 4. Validate the token
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
//UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, Arrays.asList(new SimpleGrantedAuthority("ROLE_ADMIN")));
UsernamePasswordAuthenticationToken authentication = jwtTokenUtil.getAuthentication(authToken, SecurityContextHolder.getContext().getAuthentication(), userDetails);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
logger.info("authenticated user " + username + ", setting security context");
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
// go to the next filter in the filter chain
chain.doFilter(request, response);
}
}
JwtAuthenticationEntryPoint:
#Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
String json = String.format("{\"message\": \"%s\"}", authException.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
}
}
constants:
public class Constants {
public static final long ACCESS_TOKEN_VALIDITY_SECONDS = 5*60*60;
public static final String SIGNING_KEY = "devglan123r";
public static final String TOKEN_PREFIX = "Bearer ";
public static final String HEADER_STRING = "Authorization";
public static final String AUTHORITIES_KEY = "scopes";
}
The issue is with the app uses custom JWT authentication filter which extends UsernamePasswordAuthenticationFilter which accepts user credentials and generates a long-lived JWT in return.
The issue seems to be with permitAll() which should bypass custom Authorization filter.However in debug mode I could see call to custom JwtAuthorizationFilter first instead of custom JwtAuthenticationFilter Filter which eventually results with 403 forbidden Access denied response.
Note the .antMatchers(HttpMethod.POST, "/login").permitAll() line. /login endpoint should be accessible without JWT since the JWT has not yet been generated when the user has not yet logged in.
Below is my code
JwtAuthenticationFilter.java
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
//
private AuthenticationManager authenticationManager;
private final static UrlPathHelper urlPathHelper = new UrlPathHelper();
public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
setFilterProcessesUrl("/login");
}
/**
* Trigger when we issue POST request to login / we also need to pass in
* {"username: " username, "password": password} in the request body
*/
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
// Grab credentials and map them to login viewmodel
LoginViewModel credentials = null;
try {
credentials = new ObjectMapper().readValue(request.getInputStream(), LoginViewModel.class);
} catch (IOException e) {
e.printStackTrace();
}
// Create login token
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
credentials.getUsername(), credentials.getPassword(), new ArrayList<>());
// Authenciate user
Authentication auth = authenticationManager.authenticate(authenticationToken);
return auth;
}
#Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
// Grab principal
UserPrincipal principal = (UserPrincipal) authResult.getPrincipal();
// Create JWT Token
String token = JWT.create().withSubject(principal.getUsername())
.withExpiresAt(new Date(System.currentTimeMillis() + JwtProperties.EXPIRATION_TIME))
.sign(HMAC512(JwtProperties.SECRET.getBytes()));
// add token in response
response.addHeader(JwtProperties.HEADER_STRING, JwtProperties.TOKEN_PREFIX + token);
}
#Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
logger.debug("failed authentication while attempting to access "
+ urlPathHelper.getPathWithinApplication((HttpServletRequest) request));
// Add more descriptive message
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed");
}
}
JwtAuthorizationFilter.java
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private UserRepository userRepository;
public JwtAuthorizationFilter(AuthenticationManager authenticationManager, UserRepository userRepository) {
super(authenticationManager);
this.userRepository = userRepository;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//Read the Authorization header, where the JWT token should be
String header = request.getHeader(JwtProperties.HEADER_STRING);
//If header does not contain BEARER or is null delegate to Spring impl and exit
if (header == null || !header.startsWith(JwtProperties.TOKEN_PREFIX)) {
chain.doFilter(request, response);
return;
}
// If header is present, try grab user principal from db and perform authorization
Authentication authentication = getUsernamePasswordAuthentication(request);
SecurityContextHolder.getContext().setAuthentication(authentication);
// Continue filter execution
chain.doFilter(request, response);
}
private Authentication getUsernamePasswordAuthentication(HttpServletRequest request){
String token = request.getHeader(JwtProperties.HEADER_STRING)
.replace(JwtProperties.TOKEN_PREFIX, "");
if(token !=null){
//parse the token validate it
String userName = JWT.require(Algorithm.HMAC512(JwtProperties.SECRET.getBytes()))
.build()
.verify(token)
.getSubject();
// Search in the DB if we find the user by token subject(username)
// If so, then grab user details and create auth token using username, pass, authorities/roles
if(userName != null){
User user = userRepository.findByUsername(userName);
UserPrincipal principal = new UserPrincipal(user);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userName, null, principal.getAuthorities());
return authenticationToken;
}
return null;
}
return null;
}
}
SecurityConfiguration.java
Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private UserPrincipalDetailsService userPrincipalDetailsService;
private UserRepository userRepository;
public SecurityConfiguration(UserPrincipalDetailsService userPrincipalDetailsService,
UserRepository userRepository) {
this.userPrincipalDetailsService = userPrincipalDetailsService;
this.userRepository = userRepository;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// remove csrf state in session because in jwt do not need them
.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers("/api/public/management/*").hasRole("MANAGER").antMatchers("/api/public/admin/*")
.hasRole("ADMIN").anyRequest().authenticated().and()
// add jwt filters (1. authentication, 2. authorization_)
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager(), this.userRepository));
// configure access rules
}
#Bean
DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
daoAuthenticationProvider.setUserDetailsService((UserDetailsService) this.userPrincipalDetailsService);
return daoAuthenticationProvider;
}
#Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Request,Response
Can someone suggest whats wrong here..Appreciate your help..Thanks in advance..!!!
It seems that your path is wrong. When you look at your body you can see that the path shows following: /login%0A. This seems that you have an extra character at the end of your URL. Just try to rewrite the URL in Postman.
please consider to use shouldNotFilter method from BasicAuthenticationFilter. It extends OncePerRequestFilter so you can use it in filtering class as below:
#Override
protected boolean shouldNotFilter(HttpServletRequest request) {
// code here
}
I have implemented Spring Security Auth2 with disabling password and its generating tokens and refresh tokens successfully.
My authorization server config is as follows
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
static final String CLIEN_ID = "clkey";
static final String CLIENT_SECRET = "dsds876e67ds5s67ddfdf6dfdf767843";
static final String GRANT_TYPE_PASSWORD = "password";
static final String AUTHORIZATION_CODE = "authorization_code";
static final String REFRESH_TOKEN = "refresh_token";
static final String IMPLICIT = "implicit";
static final String SCOPE_READ = "read";
static final String SCOPE_WRITE = "write";
static final String TRUST = "trust";
static final int ACCESS_TOKEN_VALIDITY_SECONDS = 1*60*60;
static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 6*60*60;
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.inMemory()
.withClient(CLIEN_ID)
.secret(CLIENT_SECRET)
.authorizedGrantTypes(GRANT_TYPE_PASSWORD, AUTHORIZATION_CODE, REFRESH_TOKEN, IMPLICIT )
.scopes(SCOPE_READ, SCOPE_WRITE, TRUST)
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS).
refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager);
}
}
I disabled password auth by my custom auth provider
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private UserService auth2;
#Autowired
public CustomAuthenticationProvider(CoreUserService coreuserservice) {
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String password = "";
String username = authentication.getName();
if(!auth2.isUserExist(username)) {
throw new BadCredentialsException("Authentication failed : bad credentials");
}
Authentication auth = new UsernamePasswordAuthenticationToken(username, password, auth2.grantAccess());
return auth;
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
And i have a custom login service and if the login found ok i want to generate the same as in Memory token and get it as json value.
my service is as
public ResponseEntity<Map<String, Object>> dologin(String email,String password) throws UsernameNotFoundException {
this.resetresponse();
this.responsedata.put("code", "200");
User user = userdao.findByUsername(email);
if(user == null)
this.responsedata.put("code", "1"); //throw new UsernameNotFoundException("Invalid username or password.");
if(user != null && !encoder.matches(password, user.getPassword()))
this.responsedata.put("code", "2"); //this.errors.add("2");
if(! "200".equals(this.responsedata.get("code"))) {
this.responsedata.put("status", "error");
}
else {
org.springframework.security.core.userdetails.User coreuser = new org.springframework.security.core.userdetails.User(user.getEmail(), "$2a$10$56PJwERx23LPIEPv.gsouOhbn50b2T/AdMV553k0uIi1LflVgD9Y6", grantAccess());
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(coreuser.getUsername(), "", coreuser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
//SecurityContextHolder.getContext().getAuthentication().getPrincipal();
this.responsedata.put("status", "success");
this.responsedata.put("data",user);
this.responsedata.put("token",authenticationToken);
}
return new ResponseEntity<Map<String, Object>>(this.responsedata,HttpStatus.OK);
}
How can we generate token and refresh token and send it with the response json entity ? Any help would be much appreciated .
You have 3 options.
After you successfully authenticated your user with spring security, you send back a redirect to the /oauth/authorize url. From there Spring Security OAuth checks that the user is authenticated and will generate the token and act based on you selected OAuth2 flow.
You can use one of the TokenGranter implementations that matches your OAuth flow. I only have example for the Client Credentials flow:
#Service
public class OauthService {
#Autowired
ClientCredentialsTokenGranter clientCredentialsTokenGranter;
public String getAuthAccessToken() {
Map<String, String> requestParameters = new HashMap<>();
requestParameters.put("scope", "read");
requestParameters.put("grant_type", OauthConst.GRANT_TYPE_CLIENT_CREDENTIALS);
Set<String> scopes = Collections.singleton("read");
TokenRequest tokenRequest = new TokenRequest(requestParameters, OauthConst.CLIENT_AUTH_ID, scopes,
OauthConst.GRANT_TYPE_CLIENT_CREDENTIALS);
OAuth2AccessToken grant = clientCredentialsTokenGranter
.grant(OauthConst.GRANT_TYPE_CLIENT_CREDENTIALS, tokenRequest);
return grant.getValue();
}
}
You can get the token programatically of a previously authenticated user by #Autowire AuthorizationServerTokenServices which has a createAccessToken method. For this to work you need to have your user authenticated previously by OAuth so you can get an OAuth2Authentication for the method call from your security context.
I'm trying to integrate SiteMinder with Spring Security. I have a logout button on home page which is supposed to make http get request to backend. I'm trying to invalidate session and redirect back to home page. It's supposed to automatically navigate to log in page which is set up in apache. unfortunate it's not invalidating session or delete cookies.
This is my
WebSecurityConfigurerAdapter
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(ssoHeaderFilter(), RequestHeaderAuthenticationFilter.class)
.authenticationProvider(ssoAuthProvider())
.logout()
.logoutUrl("/logoutPage")
.logoutSuccessUrl("/login?logout")
.deleteCookies("JSESSIONID", "GPSESSION")
.invalidateHttpSession(true)
.and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/resources/**")
.permitAll()
.antMatchers( "/**")
.hasRole("ADMIN");
}
AuthenticationController
#RequestMapping(value="/logoutPage", method = RequestMethod.GET)
public void logoutPage (HttpServletRequest request, HttpServletResponse response) throws ServletException {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
HttpSession session = request.getSession(false);
request.logout();
}
SSOAuthenticationProvider
public class SSOAuthenticationProvider implements AuthenticationProvider {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSSOAuthenticationProvider.class);
public static final Map<UserAuthority, UserAuthority> roleMap = new HashMap<>();
static {
roleMap.put(UserAuthority.ADMIN, UserAuthority.ADMIN);
}
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UserDetailsBean user = (UserDetailsBean) authentication.getPrincipal();
LOGGER.info("Inside WebSSOAuthenticationProvider authenticate method");
if(isValidRoles(user.getRoles())){
return new UsernamePasswordAuthenticationToken(user, authentication.getCredentials(),
getAuthoritiesByRoles( user.getRoles()) );
}
throw new BadCredentialsException(user.getFirstName() + " has not valid roles");
}
private boolean isValidRoles(Set<UserAuthority> roles) {
return roles != null && roles.stream().filter(roleMap::containsKey).findAny().isPresent();
}
#Override
public boolean supports(Class<?> authentication) {
return true;
}
private List<GrantedAuthority> getAuthoritiesByRoles(Set<UserAuthority> roles) {
List<GrantedAuthority> authorities = roles.stream().map(v -> v.name()).map(SimpleGrantedAuthority::new).collect(Collectors.toList());
return authorities;
}
SSOAuthenticationFilter
public class SSOAuthenticationFilter extends RequestHeaderAuthenticationFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(SSOAuthenticationFilter.class);
#Value("${project.ui.test.mode:false}")
private boolean guiTestMode;
private String FIRST_NAME = "sso-givenname";
private String LAST_NAME = "sso_surname";
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
LOGGER.debug("Working in the test mode for logging");
Enumeration<String> names = request.getHeaderNames();
LOGGER.info("Going to print all http headers");
while (names.hasMoreElements()) {
String name = names.nextElement();
String value = request.getHeader(name);
LOGGER.info("name: " + name + ", value: " + value);
}
UserDetailsBean user = new UserDetailsBean();
if(isTestMode()){
user.setFirstName("Mock");
user.setLastName("User");
user.setRoles(new HashSet<UserAuthority>(Arrays.asList(UserAuthority.ROLE_P_AND_S)));
request.getSession().setAttribute("user" , user);
return user;
}
user.setFirstName(request.getHeader(FIRST_NAME));
user.setLastName(request.getHeader(LAST_NAME));
user.setRoles(new HashSet<UserAuthority>(Arrays.asList(UserAuthority.ROLE_P_AND_S)));
request.getSession().setAttribute("user" , user);
return user;
}
private boolean isTestMode() {
return guiTestMode;
}
}