I am trying to authenticate a request from using spring security, I have followed few blogs and videos but i am not able to fix the issues.
Security configuration is loaded but my requests are not getting authenticated. I get 403 error
Bean class in WebSecurityConfigureAdapter
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userService);
System.out.println(userDetailsService());
return authProvider;
}
over riding configure method
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().antMatchers("/products","/orders").hasAnyRole("admin");//.authenticated();
}
user details services overridding loadUserByUsername
#Override
public UserDetails loadUserByUsername(String loginName) {
String authenticated = "false";
UserDetails userDetails = null;
List<Users> usersList = usersRepository.findByLoginName(loginName);
if(usersList.size()==0) {
return null;
}else {
for (Users users : usersList) {
List<Roles> rolesList = users.getRoles();
for (Roles roles : rolesList) {
if(roles.getUserRole().equalsIgnoreCase("admin")) {
authenticated = "admin";
GrantedAuthority authority = new SimpleGrantedAuthority(authenticated);
User user = new User(users.getLoginName(),users.getPassword(),Arrays.asList(authority));
userDetails = (UserDetails)user;
}
}
}
return userDetails;
}
using postman setting up the credentials as basicAuth, its throwing 403 error
the issue was...
we need to either set the value for different getters in userdetails class or explicitly set to true
i have below getters explicitly set it to true, it worked for me
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
Related
Whenever I'm trying to login, I'm getting this error 'You do not have access to the Apex class named LightningLoginFormController'. I've made changes in Apex Class Security and added all the profiles, also checked by using 'with sharing' and 'without sharing', but it's still not working.
global class LightningLoginFormController {
public LightningLoginFormController() {
}
#AuraEnabled
public static String login(String username, String password, String startUrl) {
try{
ApexPages.PageReference lgn = Site.login(username, password, startUrl);
aura.redirect(lgn);
return null;
}
catch (Exception ex) {
return ex.getMessage();
}
}
#AuraEnabled
public static Boolean getIsUsernamePasswordEnabled() {
Auth.AuthConfiguration authConfig = getAuthConfig();
return authConfig.getUsernamePasswordEnabled();
}
#AuraEnabled
public static Boolean getIsSelfRegistrationEnabled() {
Auth.AuthConfiguration authConfig = getAuthConfig();
return authConfig.getSelfRegistrationEnabled();
}
#AuraEnabled
public static String getSelfRegistrationUrl() {
Auth.AuthConfiguration authConfig = getAuthConfig();
if (authConfig.getSelfRegistrationEnabled()) {
return authConfig.getSelfRegistrationUrl();
}
return null;
}
#AuraEnabled
public static String getForgotPasswordUrl() {
Auth.AuthConfiguration authConfig = getAuthConfig();
return authConfig.getForgotPasswordUrl();
}
#TestVisible
private static Auth.AuthConfiguration getAuthConfig(){
Id networkId = Network.getNetworkId();
Auth.AuthConfiguration authConfig = new Auth.AuthConfiguration(networkId,'');
return authConfig;
}
#AuraEnabled
global static String setExperienceId(String expId) {
// Return null if there is no error, else it will return the error message
try {
if (expId != null) {
Site.setExperienceId(expId);
}
return null;
} catch (Exception ex) {
return ex.getMessage();
}
}
}
For this to work, you need to add the Apex Class in Community Portal User Profile i.e. the Profile using "Guest User License".
Navigation: All sites --> Workspaces --> Administration --> Pages --> Go to Force.com --> Public Access Settings --> Enabled Apex Class Access.
And if you have a custom function in your Apex Class that is using SOQL query, for that to fetch data you will need to use "without sharing" security settings!!
I'm implementing JWT and Spring Security for authentication in my application.
I have 3 roles: Admin, Moderator and User.
For example, after logging with user role, I got the home page, but once I go to hit the user space by clicking on a button, I got:
2020-09-04 09:01:22.819 ERROR 10148 --- [nio-8080-exec-5]
c.b.s.security.jwt.AuthEntryPointJwt : Unauthorized error: Full
authentication is required to access this resource
the file webSecurityConfig.java is:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(
// securedEnabled = true,
// jsr250Enabled = true,
prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsServiceImpl userDetailsService;
#Autowired
private AuthEntryPointJwt unauthorizedHandler;
#Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
return new AuthTokenFilter();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.authorizeRequests().antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/test/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
The class AuthEntryPointJwt is:
#Component
public class AuthEntryPointJwt implements AuthenticationEntryPoint {
private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
logger.error("Unauthorized error: {}", authException.getMessage());
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized");
}
}
The class AuthTokenFilter is:
public class AuthTokenFilter extends OncePerRequestFilter {
#Autowired
private JwtUtils jwtUtils;
#Autowired
private UserDetailsServiceImpl userDetailsService;
private static final Logger logger = LoggerFactory.getLogger(AuthTokenFilter.class);
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
String username = jwtUtils.getUserNameFromJwtToken(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Cannot set user authentication: {}", e);
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7, headerAuth.length());
}
return null;
}
}
The class JwtUtils is:
#Component
public class JwtUtils {
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
#Value("${bezkoder.app.jwtSecret}")
private String jwtSecret;
#Value("${bezkoder.app.jwtExpirationMs}")
private int jwtExpirationMs;
public String generateJwtToken(Authentication authentication) {
UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
return Jwts.builder()
.setSubject((userPrincipal.getUsername()))
.setIssuedAt(new Date())
.setExpiration(new Date((new Date()).getTime() + jwtExpirationMs))
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String getUserNameFromJwtToken(String token) {
return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
}
public boolean validateJwtToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException e) {
logger.error("Invalid JWT signature: {}", e.getMessage());
} catch (MalformedJwtException e) {
logger.error("Invalid JWT token: {}", e.getMessage());
} catch (ExpiredJwtException e) {
logger.error("JWT token is expired: {}", e.getMessage());
} catch (UnsupportedJwtException e) {
logger.error("JWT token is unsupported: {}", e.getMessage());
} catch (IllegalArgumentException e) {
logger.error("JWT claims string is empty: {}", e.getMessage());
}
return false;
}
}
The class AuthController is:
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
#RequestMapping("/api/auth")
public class AuthController {
#Autowired
AuthenticationManager authenticationManager;
#Autowired
UserRepository userRepository;
#Autowired
RoleRepository roleRepository;
#Autowired
PasswordEncoder encoder;
#Autowired
JwtUtils jwtUtils;
#PostMapping("/signin")
public ResponseEntity<?> authenticateUser(#Valid #RequestBody LoginRequest loginRequest) {
System.out.println("---------------- auth 1 ");
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
String jwt = jwtUtils.generateJwtToken(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
List<String> roles = userDetails.getAuthorities().stream()
.map(item -> item.getAuthority())
.collect(Collectors.toList());
return ResponseEntity.ok(new JwtResponse(jwt,
userDetails.getId(),
userDetails.getUsername(),
userDetails.getEmail(),
roles));
}
#GetMapping("/user")
#PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
public String userAccess()
{
System.out.println("---------------- test User ");
return "User Content.";
}
}
The file application.properties, I put:
spring.datasource.url=...
spring.datasource.username=...
spring.datasource.password=...
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation= true
spring.jpa.properties.hibernate.dialect=...
spring.jpa.hibernate.ddl-auto=update
bezkoder.app.jwtSecret= bezKoderSecretKey
bezkoder.app.jwtExpirationMs= 86400000
In Browser console, I got that exception.
Could you please help me solving that issue ?. Big thanks.
You have to update WebSecurityConfig according to your Controller mapping.
.antMatchers("/**").permitAll()
.anyRequest().authenticated();
You probably are testing in wrong in postman or amneasia or whatever API testing tool you are using. If you are using postman :
1.Select the Header tab
2.Click the button that says "hide auto-generated headers"
3.Type "Authorization" (without quotes) under the "Key" column.
4.Type "Bearer" under the "Value" column then paste the token. Ensure there is a space between "Bearer" and the "Token".
5.Send the request
You should comment #PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')").
Tell about the result ?.
Before I activated the critical update name 'Restrict Access to #AuraEnabled Apex Methods for Guest and Portal Users Based on User Profile' I can login to Portal community properly.
After I Activated that critical update, community user can not login to Portal and the error message in response of Network tab will be 'You do not have access to the Apex class named LightningLoginFormController
I am sure that guest or portal user profile can access LightningLoginFormController Apex class.
global class LightningLoginFormController {
public LightningLoginFormController() {
}
#AuraEnabled
public static String login(String username, String password, String startUrl) {
try{
ApexPages.PageReference lgn = Site.login(username, password, startUrl);
aura.redirect(lgn);
return null;
}
catch (Exception ex) {
return ex.getMessage();
}
}
#AuraEnabled
public static Boolean getIsUsernamePasswordEnabled() {
Auth.AuthConfiguration authConfig = getAuthConfig();
return authConfig.getUsernamePasswordEnabled();
}
#AuraEnabled
public static Boolean getIsSelfRegistrationEnabled() {
Auth.AuthConfiguration authConfig = getAuthConfig();
return authConfig.getSelfRegistrationEnabled();
}
#AuraEnabled
public static String getSelfRegistrationUrl() {
Auth.AuthConfiguration authConfig = getAuthConfig();
if (authConfig.getSelfRegistrationEnabled()) {
return authConfig.getSelfRegistrationUrl();
}
return null;
}
#AuraEnabled
public static String getForgotPasswordUrl() {
Auth.AuthConfiguration authConfig = getAuthConfig();
return authConfig.getForgotPasswordUrl();
}
#TestVisible
private static Auth.AuthConfiguration getAuthConfig(){
Id networkId = Network.getNetworkId();
Auth.AuthConfiguration authConfig = new Auth.AuthConfiguration(networkId,'');
return authConfig;
}
#AuraEnabled
global static String setExperienceId(String expId) {
// Return null if there is no error, else it will return the error message
try {
if (expId != null) {
Site.setExperienceId(expId);
}
return null;
} catch (Exception ex) {
return ex.getMessage();
}
}
}
Does someone else face the same problem?
You need to explicitly allow access to Apex classes containing #AuraEnabled methods.
Guest, portal, or community user can access an #AuraEnabled Apex method only when the user’s profile or an assigned permission set allows access to the Apex class.
More details here:
https://releasenotes.docs.salesforce.com/en-us/spring20/release-notes/rn_lc_restrict_apex_guest_users.htm
Use logged throuth the JSP form:
#ManagedBean
#SessionScoped
public class LoginView {
private String username; //+getter +setter
private String password; //+getter +setter
public String submit()
{
try {
HttpServletRequest request = (HttpServletRequest) FacesContext
.getCurrentInstance()
.getExternalContext()
.getRequest();
request.login(username, password);
} catch (ServletException e) {
FacesContext.getCurrentInstance().addMessage("login-form:username",
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Validation Error", "Incorrect login or password"));
return "/login";
}
return "/index?faces-redirect=true";
}
}
How to check user logged in thorug EJB?
Below example whan I need:
#Stateless
public class SessionServiceBean {
#Resource
SessionContext sessionContext;
#EJB
UserService userService;
#Produces
#Named
#LoggedIn
public User getLoggedUser() {
if (/* check user is logged */) {
return userService.getByName(sessionContext.getCallerPrincipal().getName());
}
}
}
I foun only that the not logged use has name "anonymous", but it not better way, I think.
You are using a #Stateless EJB. In a Stateless EJB, you are working with user sessions. This is probably not right and the bean should be #Stateful.
I see no point in using EJBs in your example. Using pure CDI bean annotated with #SessionScoped would be sufficient.
You can store you user's session information directly in a session scoped bean. There is not need to use HttpServletRequest. For example:
#Named
#SessionScoped
public class UserSession implements Serializable {
private User activeUser;
public void logIn(User user) {
this.activeUser = user;
}
public void logOut() {
activeUser = null;
}
public boolean isLoggedIn() {
return activeUser != null;
}
public User getActiveUser() {
return activeUser;
}
}
I had been developing and testing on the Codename One simulator and everything worked fine.
However, when I tested it on a real Android device, I get a 405 Method Not Allowed error. This happened on both a POST and GET request.
I suspect it is the #Consume and #Produces which are causing the problem. How do I fix this?
Here are my server side code:
#GET
#Path("/all/{language}")
#Produces("application/json")
public final Response getAllCelebrities(#PathParam("language") String language) {
String celebritiesJSONString = CelebrityActions.getAllCelebritiesNamesJSONString(language);
return Response.ok(celebritiesJSONString).build();
}
#POST
#Path("/login")
#Consumes("application/x-www-form-urlencoded")
#Produces("text/plain")
public final Response login(
#FormParam("loginid") String loginid,
#FormParam("password") String password
) {
System.out.println("login 0 started");
Long fanID;
try {
fanID = AccountsActions.login(loginid, password);
} catch (Exception e) {
return Response.serverError().entity(e.getMessage()).build();
}
if (fanID == null) {
return responseFanIDNotFoundError();
}
System.out.println("This is printed out!!!");
System.out.println("login 100 ended");
return Response.ok().build();
}
And here's my log upon execution of the login() method:
login 0 started
This is printed out!!!
login 100 ended
which means the server side method was ready to return a 200 response.
What is causing the Android client to show a 405 Method Not Allow error?
EDIT: I'm adding my cient-side code here:
(note that this one handles a cookie from a server)
public class Login extends PostConnection {
private final String LoginEndpoint = "account/login";
private String loginIDString;
private String loginPasswordString;
// Tested and works on simulator!
public Login(String loginIDString, String loginPasswordString) {
super();
endpoint = LoginEndpoint;
this.loginIDString = loginIDString;
this.loginPasswordString = loginPasswordString;
}
#Override
protected void prepareParametersMap() {
parametersMap = new HashMap<>();
parametersMap.put("loginid", loginIDString);
parametersMap.put("password", loginPasswordString);
}
}
public abstract class PostConnection extends PostPutConnection {
public PostConnection() {
super();
}
public boolean connect() throws IOException {
connectionRequest.setHttpMethod("POST");
return super.connect();
}
}
public abstract class PostPutConnection extends Connection {
protected HashMap<String, String> parametersMap;
public PostPutConnection() {
super();
}
protected static final void setPostParameters(ConnectionRequest connectionRequest, HashMap<String, String> parametersMap) {
Set<String> paramateterKeys = parametersMap.keySet();
Iterator<String> parameterKeysIterator = paramateterKeys.iterator();
while (parameterKeysIterator.hasNext()) {
String key = parameterKeysIterator.next();
String value = parametersMap.get(key);
connectionRequest.addArgument(key, value);
}
}
protected abstract void prepareParametersMap();
public boolean connect() throws IOException {
prepareParametersMap();
setPost();
setPostParameters();
return super.connect();
}
private void setPostParameters() {
setPostParameters(connectionRequest, parametersMap);
}
private final void setPost() {
connectionRequest.setPost(true);
}
}
public abstract class Connection {
private final static String protocol = "http";
private final static String domain = "192.168.0.109:20000";
protected ConnectionRequest connectionRequest;
protected String endpoint;
public Connection() {
super();
init();
}
protected void init() {
connectionRequest = new ConnectionRequest();
connectionRequest.setCookiesEnabled(true);
ConnectionRequest.setUseNativeCookieStore(true);
}
public boolean connect() throws IOException {
connectionRequest.setUrl(protocol + "://" + domain + "/" + endpoint);
NetworkManager.getInstance().addToQueueAndWait(connectionRequest);
int responseCode = getResponseCode();
return responseCode == 200 ? true : false;
}
private int getResponseCode() {
int responseCode = connectionRequest.getResponseCode();
return responseCode;
}
}
And another method below:
(note that this one does not handle cookies)
public class GetAllCelebrities extends GetConnection {
private final String GetCelebritiesEndpoint = "celebrity/all";
public GetAllCelebrities(String language) {
super();
endpoint = GetCelebritiesEndpoint + "/" + language;
}
}
public abstract class GetConnection extends Connection {
private Map<String, Object> responseData;
public GetConnection() {
super();
}
public boolean connect() throws IOException {
connectionRequest.setHttpMethod("GET");
boolean connectResult = super.connect();
if (!connectResult) {
return false;
}
responseData = getResponseResult();
return true;
}
private Map<String, Object> getResponseResult() throws IOException {
byte[] responseData = connectionRequest.getResponseData();
ByteArrayInputStream responseDataBAIS = new ByteArrayInputStream(responseData);
InputStreamReader responseDataISR = new InputStreamReader(responseDataBAIS, "UTF-8");
JSONParser responseDateJSONParser = new JSONParser();
Map<String, Object> responseResult = responseDateJSONParser.parseJSON(responseDataISR);
return responseResult;
}
public Map<String, Object> getResponseData() {
return responseData;
}
}
And it is called like:
private Map<String, Object> fetchCelebrities() throws IOException {
GetAllCelebrities getAllCelebrities = new GetAllCelebrities("en");
getAllCelebrities.connect();
return getAllCelebrities.getResponseData();
}
private boolean performLogin() throws IOException {
String loginIDString = loginID.getText();
String loginPasswordString = loginPassword.getText();
Login login = new Login(loginIDString, loginPasswordString);
boolean loginResult = login.connect();
return loginResult;
}
It's a bit hard to read all of this code but I'll venture a guess based on the server message. You've set the method to "PUT" along the way in the post put class and that isn't supported by the server yet.
The best way to debug these things is with the network monitor in the Simulator. Its shows the traffic and would have made these things mostly clear