Spring Boot error 403 when sending request - java

My system consists of 3 parts. Spring Boot app, Angular app and Android app. When my Android app sends request to reset password (if don't remember it) then Spring Boot app receives this request with this method:
#PostMapping("/forgot_password")
public String processForgotPassword(#RequestBody String email, HttpServletRequest request) {
try {
String token = RandomString.make(30);
userService.updateResetPasswordToken(token, email);
String resetPasswordLink = "https://jakuwegiel.web.app/projects/endless-blow/reset-password?token=" + token;
sendEmail(email, resetPasswordLink);
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
return "Email has been sent";
}
where updateResetPasswordToken() is:
public void updateResetPasswordToken(String token, String email) throws SQLException {
UserDetails userDetails = usersDao.getUserDetailsByEmail(connFromUserService, email);
if (userDetails != null) {
User user = usersDao.getUserByName(connFromUserService, userDetails.getName());
usersDao.setResetPasswordToken(connFromUserService, user, token);
}
}
where getUserDetailsByEmail() is:
public UserDetails getUserDetailsByEmail(Connection connection, String email) {
UserDetails userDetails = new UserDetails();
PreparedStatement ps;
try {
ps = connection.prepareStatement("SELECT * from users_details where email = '"+email+"'");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
userDetails = new UserDetails(rs.getString(2), rs.getString(3));
}
} catch (SQLException e) {
e.printStackTrace();
}
return userDetails;
}
and getUserByName() is:
public User getUserByName(Connection connection, String name) throws SQLException {
User user = new User();
PreparedStatement ps;
try {
ps = connection.prepareStatement("SELECT * from users where username = '"+name+"'");
ResultSet rs = ps.executeQuery();
if (rs.next()) {
user = new User(rs.getString(2), rs.getString(3));
}
} catch (SQLException e) {
e.printStackTrace();
}
return user;
}
and setResetPasswordToken() is:
public void setResetPasswordToken(Connection connection, User user, String token) {
PreparedStatement ps;
try {
ps = connection.prepareStatement("UPDATE users set reset_password_token = '" + token + "' where username = '" + user.getUsername() + "'");
ps.executeUpdate();
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
Then user receives reset link on email. He opens link which opens Angular app and I input on two EditText fields passsword twice and send with button send which performs this code:
public postResetPassword(token: string, password: string): Observable<any> {
const body = {
'token': token,
'password': password
};
const headers = new HttpHeaders().set('Content-Type', 'application/json; charset=utf-8');
return this.http.post<any>('https://endlessblow-1.herokuapp.com/reset_password', body,{headers: headers});
}
What's strange this Angular's code generates OPTIONS request not POST why?
Spring Boot app receives this request with method:
#RequestMapping(value = "/reset_password", consumes="application/json", method = {RequestMethod.OPTIONS, RequestMethod.POST, RequestMethod.GET})
public String processResetPassword(#RequestBody TokenAndPassword tokenAndPassword) {
try {
User user = userService.getByResetPasswordToken(tokenAndPassword.getToken());
if (user == null) {
return "message1";
} else {
userService.updatePassword(user, tokenAndPassword.getPassword());
System.out.println("You have successfully changed your password.");
}
}
catch (Exception ex) {
System.out.println(ex.getMessage());
return "Error. Did you use your token already? Or does this link come from email?";
}
return "You have successfully changed your password.";
}
But finally every time /reset_password comes then ERROR 403 shows up.
I also added such class:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().anyRequest().permitAll();
}
}
but it gives nothing.
What's wrong with this code?
Thank you very very very much for help!

The issue is most likely with the preflight request that the angular app is sending. You could disable cors by adding this to your security configuration:
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("*");
}
or you can configure it to your use case by following the example from the spring documentation https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#mvc-cors-global-java

Related

How to authenticate Springboot API based on User role that is being fetched from DB

I have user role saved in DB, so based on role ID authentication has to be done for each API.
I tried few approaches but did not succeed.
Code Snippet:
CONTROLLER:
#PreAuthorize("#AuthService.getUserRole(1)")
#PostMapping(name = "POST - Save the user info to Automation Counter table", path = "userlogininfo")
#Operation(summary = "Save the user info to Automation Counter table")
public ResponseEntity<ApiResponse> saveUserInfoinDB(#RequestBody SaveUserInfo saveUserInfoDetails, Principal principal)
{
try
{
saveUserInfoDetails.ntid = principal.getName().split("\\n")[0];
if (saveUserInfoDetails.firstName != null && saveUserInfoDetails.lastName != null && saveUserInfoDetails.ntid != null && saveUserInfoDetails.applicationName !=null)
{
ApiResponse apiResponse = restService.osmAPIPost("/dashboard/userlogininfo",saveUserInfoDetails);
return ResponseEntity.ok(apiResponse);
}
else
{
ApiResponse response = new ApiResponse<>(false, "Did not receive data in correct format", null);
return ResponseEntity.badRequest().body(response);
}
}
catch (Exception ex)
{
String logMessage = SharedUtils.logErrorMessage(this.getClass().getSimpleName(), ex, "Error while saving user info in DB.");
ApiResponse response = new ApiResponse<>(false, logMessage, null);
return ResponseEntity.badRequest().body(response);
}
}
SERVICE:
#Service
public class AuthService {
private final AuthRepository authRepository;
public AuthService(AuthRepository authRepository)
{
this.authRepository = authRepository;
}
public boolean getUserRole(int roleId)
{
try
{
boolean userRole=authRepository.getUserLoginInfo(Principal.class.getName().split("\\n")[0],roleId);
return userRole;
}
catch (Exception ex)
{
throw ex;
}
}
}
REPOSITORY:
#Transactional
public boolean getUserLoginInfo(String ntid, int roleId )
{
try
{
String returnValue=null;
String sql = "SELECT Alias FROM Users where NTID=? AND RoleId=?" ;
returnValue= osmv2JdbcTemplate.queryForObject(sql,String.class,ntid,roleId);
if(returnValue!=null)
{
return true;
}
else
{
return false;
}
}
catch(Exception ex)
{
String logMessage = SharedUtils.logErrorMessage(this.getClass().getSimpleName(), ex, "Could not fetch the User alias based on role ID and NTID from DB");
LOGGER.info(logMessage);
return false;
}
}
I have created custom method that will be used in #PreAuthorize but it did not work. The method is fetching the role id from DB based on username.Please help.. Thanks in advance:)

Redirect the page according to the user role after validating user [duplicate]

This question already has answers here:
How do I compare strings in Java?
(23 answers)
Closed 1 year ago.
I have been trying to redirect the user that has logged into the system to their respective page after checking their email and password. But i am not sure about the logic behind that coding and when i try it it just response with the else statement. I have tried the validation of the email and password and that works fine and redirects to the correct page, but when i add the user type condition it doesnt work
I have tried including nested if statements, but i am not sure about its logic,it always executes the else statement.
loginControllerServlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String email=request.getParameter("email");
String password=request.getParameter("pwrd");
User theUser=loginDbUtil.gettype(email);
if(loginDbUtil.check(email, password))
{
String p="pharmacist";
if(theUser.getType()==p)
{
// HttpSession session=request.getSession();
// session.setAttribute("email", email);
response.sendRedirect("medicine.jsp");
}
else
{
response.sendRedirect("index.jsp");
}
}else
{
response.sendRedirect("index.jsp");
}
}
}
loginDbUtil.java
public boolean check(String email,String password)
{
Connection myConn=null;
PreparedStatement myStmt=null;
ResultSet rs=null;
try
{
//get db connection
myConn=dataSource.getConnection();
//sql statemtn
String sql="select email,pass from usertab where email=? and pass=? ";
myStmt=myConn.prepareStatement(sql);
//set the param values for user
myStmt.setString(1, email);
myStmt.setString(2, password);
rs=myStmt.executeQuery();
if(rs.next())
{
return true;
}
}catch(Exception e)
{
e.printStackTrace();
}
return false;
}
public User gettype(String email) {
User type=null;
Connection myConn=null;
PreparedStatement myStmt=null;
ResultSet rs=null;
try
{
//get db connection
myConn=dataSource.getConnection();
//sql statemtn
String sql="select type from usertab where email=? ";
myStmt=myConn.prepareStatement(sql);
//set the param values for user
myStmt.setString(1, email);
rs=myStmt.executeQuery();
if(rs.next())
{
String t=rs.getString("type");
type =new User(t);
}
}catch(Exception e)
{
e.printStackTrace();
}
return type;
}
}
What i want is after the email and password is checked then next check for the users data type and redirect them to the correct page
In your loginControllerServlet.java change this to
if(theUser.getType()==p)
to this
if(theUser.getType().equals(p))
According to your logic, I think first of all, you should put the attribute type as an int, there will be less chances to have a type like Pharmacist and pharmacist.
Then to communicate with database check is correct, but I don't think the same about you getType method, this is what I would suggest to you :
1st : Create one bean (object) User as you seem to have done and put getters and setters, and put a constructor with all the attributes as parameters.
Example :
class User {
private int id;
private String mail;
private String password;
private int type;
public User() {
}
public User(int id, String mail, String password, int type) {
this.id = id;
this.mail = mail;
this.password = password;
this.type = type;
}
// Getters and setters
}
Then you check mail would return directly a user object, so you should have
class userDB {
public User login(String mail, String password) {
User user = new User();
String query = "select * from user where mail = ? and password = ?";
try {
PreparedStatement prep = conn.prepareStatement(query);
prep.setString(1,mail);
prep.setString(2,password);
ResultSet res = res.executeQuery();
if(res.first) {
user = new User(res.getInt("id"),res.getString("mail"),res.getString("password"),res.getInt("type"));
return user;
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
And then in you Servlet you can write:
class Login extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse respons) throws ServletException, IOException {
this.getServletContext().getRequestDispatcher(your_jsp_relative_path).forward(request, respons);
}
public void doPost(HttpServletRequest request, HttpServletResponse respons) throws ServletException, IOException {
String mail = (String) request.getParameter("mail');
String password = (String) request.getParameter("mail');
User user = new UserDB().login(mail, password);
if(user != null){
int type = user.getType();
switch(type){
case 0 :
respons.sendRedirect("type_0_page");
break;
case 1 :
respons.sendRedirect("type_1_page");
break;
case 2 :
respons.sendRedirect("type_2_page");
break;
default :
repons.sendRedirect("/error500.jsp");
break;
}
}else{
this.getServletContext().getRequestDispatcher(your_jsp_relative_path).forward(request, respons);
}
}
}

Add ListView everytime a new entry in database is made

I need help for my student project.
Currently I'm making a chat application using JavaFX, MQTT and Mysql.
I want to make an userlist (which user are online)
I tried this so that a new user would send a message through a special topic, and all will be "secretely" subscribing this topic it'll receive the message (using callback) and call the method insertingMysql() and updatingList(). But somehow it does not work.
This is where I give the username
public static void logging(String username) throws MqttException {
if(username != null && !username.isEmpty()) {
MysqlDatabase.insertingMysql(username);
window.close();
}
}
This is to add ListView
public static void addItem(String item) {
userList.getItems().add(item);
}
This is the part where I have problems with (I think)
public class MysqlDatabase {
static String topic_body = "chat";;
static String topic_ext = "userList";
static MqttCallback sqlCallback = new MqttCallback() {
#Override
public void connectionLost(Throwable thrwbl) {
}
#Override
public void messageArrived(String string, MqttMessage mm) throws Exception {
String username = new String(mm.getPayload());
System.out.println(username); // Checking
updatingList();
}
#Override
public void deliveryComplete(IMqttDeliveryToken imdt) {
}
};
public static void sqlConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("Connected");
}
public static void mqttConnection(String username) throws MqttException {
MqttClient sqlClient = new MqttClient("tcp://iot.eclipse.org:1883", username);
sqlClient.connect();
sqlClient.subscribe(topic_body + topic_ext + "#");
sqlClient.setCallback(sqlCallback);
MqttMessage sqlMessage = new MqttMessage();
sqlMessage.setPayload((username).getBytes());
sqlClient.publish(topic_body + "userlist", sqlMessage);
}
public static void insertingMysql(String username) throws MqttException {
sqlConnection();
String host = "jdbc:mysql://localhost/test";
String user = "root";
String password = "";
try {
Connection connect = DriverManager.getConnection(host, user, password);
PreparedStatement statement = (PreparedStatement) connect.prepareStatement("INSERT INTO chat_test(username)VALUES(?)");
statement.setString(1, username);
statement.executeUpdate();
statement.close();
System.out.println("Inserted to database!");
mqttConnection(username);
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.println("Error");
e.printStackTrace();
}
}
public static void updatingList() {
sqlConnection();
String host = "jdbc:mysql://localhost/test";
String user = "root";
String password = "";
try {
Connection connect = DriverManager.getConnection(host, user, password);
PreparedStatement statement = (PreparedStatement) connect.prepareStatement("SELECT * FROM chat_test");
ResultSet rs = statement.executeQuery();
while(rs.next()) {
String uName = rs.getString("username");
System.out.println("Username: " + uName);
JavaFXChat.addItem(uName);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
System.out.println("Error");
e.printStackTrace();
}
}
}
There are 2 options here:
Database triggers. This is code that runs on the Database server when ever a table is changed. Normally these triggers are SQL statements but this blog (https://patternbuffer.wordpress.com/2012/09/14/triggering-shell-script-from-mysql/) post seams to talk about a plugin to support running a script that could send a MQTT message announcing the new user coming online.
Use MQTT messages and the Last Will and Testament feature. A users would publish a message to known topic then they come online, they would publish a similar message when going offline. The LWT can be used to automatically publish the offline message if the connetion drops due to problem. (details of LWT can be found here http://www.hivemq.com/blog/mqtt-essentials-part-9-last-will-and-testament

Identifying the current user requesting the rest endpoints

I am trying to implement authentication and authorization using angular and java where I came across "identifying the current user asking for resource" from this Link
The point where I am not able to understand is getting the user from getUserPrincipal() method using jax-rs SecurityContext.
Security Context:
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return email;
}
};
}
}
The above method apparently returns a user but the question is from where? I have searched on this topic but no where I see code for fetching the user from DB or any other resource.
Where as I have implemented some thing like this for validation:
#Secured
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Get the HTTP Authorization header from the request
String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
// Check if the HTTP Authorization header is present and formatted correctly
if (authorizationHeader == null || !authorizationHeader.startsWith("Basic ")) {
throw new NotAuthorizedException("Authorization header must be provided");
}
// Extract the token and email address from the HTTP Authorization header
String email = requestContext.getHeaderString("Email");
String token = authorizationHeader.substring("Basic".length()).trim();
try {
// Validate the token
validateToken(email, token);
} catch (Exception e) {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).build());
}
private void validateToken(String email, String token) throws Exception {
// Check if it was issued by the server and if it's not expired
// Throw an Exception if the token is invalid
try {
TokenSaverAndValidatorDAO tokenValidator = new TokenSaverAndValidatorDAO();
String result = tokenValidator.checkTokenFromDB(email, token);
if (result.equals(token)) {
System.out.println("Token is same");
} else {
System.out.println("Token is not same");
throw new Exception();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
The above method validateToken() calls a method from DAO class for validation:
public String checkTokenFromDB(String email, String token) {
String result = "";
try {
String query = "Select USR_TOKEN From TBL_USER where USR_PRIMARY_EMAIL= ? ";
Connection con = DBConnection.getConnection();
PreparedStatement statement = con.prepareStatement(query);
statement.setString(1, email);
ResultSet rs = statement.executeQuery();
while (rs.next()) {
result = rs.getString("USR_TOKEN");
}
} catch (Exception ex) {
System.out.println("Error in TokenSaverDAO class");
ex.printStackTrace();
}
return result;
}
How do I use jax-rs securityContext in my application. Does it apply to my scenario?
I am sending the headers from angular like this:
$httpProvider.interceptors.push(['$q', '$location', '$localStorage', 'jwtHelper', function ($q, $location, $localStorage, jwtHelper) {
return {
'request': function (config) {
config.headers = config.headers || {};
if ($localStorage.token) {
var decodeToken = jwtHelper.decodeToken($localStorage.token);
config.headers.Email = decodeToken.email;
config.headers.Authorization = 'Basic ' + $localStorage.token;
}
return config;
},
'responseError': function (response) {
if (response.status === 401 || response.status === 403) {
$location.path('/Login');
}
return $q.reject(response);
}
};
}]);
Where the headers are set like this:
config.headers.Email = decodeToken.email;
config.headers.Authorization = 'Basic ' + $localStorage.token;

Prevent multiple user login - HttpSessionBindingListener

I have a web application where I want to prevent a user login multiple times (from different browsers on the same machine or from different machines).
I read about HttpSessionBindingListener and I tried to adapt my login servlet and my user Bean to implement the desired solution. Unfortunately it only works when I login the second time on the same browser (in a different tab) but if I change browser (on the same machine) it doesn't work anymore.
The code is as follows.
User Bean to put in session after successful login in
public class BeanUtente implements HttpSessionBindingListener {
private String username;
private String gruppo;
public boolean ruoloPresente(String nomeRuolo) {
//se il gruppo dell'utente รจ uguale a quello richiesto dal filtro
if (this.gruppo.equals(nomeRuolo))
return true;
else
return false;
}
public void valueBound(HttpSessionBindingEvent argo) {
System.out.println("Value Bound Called, " + argo.getValue() + " isNewSession: " + argo.getSession().isNew());
}
public void valueUnbound(HttpSessionBindingEvent argo) {
System.out.println("Value UnBound Called, " + argo.getValue() + " isNewSession: " + argo.getSession().isNew());
}
public String toString() {
return "Username is: " + username;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getGruppo() {
return gruppo;
}
public void setGruppo(String gruppo) {
this.gruppo = gruppo;
}
}
Login servlet
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Db db = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
response.setHeader("Cache-Control","no-cache,no-store,must-revalidate");
response.setHeader("Pragma","no-cache");
response.setDateHeader("Expires", 0);
Locale locale = request.getLocale();
ResourceBundle labels = ResourceBundle.getBundle("risorse.label", locale);
String urlLoginOk = getInitParameter("urlLoginOk");
String urlLoginKo = getInitParameter("urlLoginKo");
String username = request.getParameter("username");
String password = request.getParameter("password");
db = new Db();
db.apriConnessione();
String sql = "SELECT gruppo FROM Utenti WHERE username=? AND password=SHA2(?, 512)";
ps = db.getConnection().prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
rs = ps.executeQuery();
//login OK
if(username != null && password != null && rs.next()) {
BeanUtente beanUtente = new BeanUtente();
beanUtente.setUsername(username);
beanUtente.setGruppo(rs.getString("gruppo"));
HttpSession sess = request.getSession();
sess.setAttribute("beanUtente", beanUtente);
request.getRequestDispatcher(urlLoginOk).forward(request, response);
}
//login KO
else {
request.setAttribute("errore", labels.getString("loginFallito"));
request.getRequestDispatcher(urlLoginKo).forward(request, response);
}
}
catch(Exception e) {
e.printStackTrace();
}
finally {
try {
if(!ps.isClosed())
ps.close();
if(!rs.isClosed())
rs.close();
}
catch (SQLException sqle) {
sqle.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
if(db.getConnection() != null)
db.chiudiConnessione();
}
}
}
Here is the log.
When I login the first time, I get:
*Value Bound Called, Username is: pi isNewSession: false*
When I login the second time from the same browser, I get:
*Value Bound Called, Username is: pi isNewSession: false
Value UnBound Called, null isNewSession: false*
So it seems the UnBound method is correctly called.
However, if I login the third time from another browser on the same machine, I get:
*Value Bound Called, Username is: pi isNewSession: false*
that is, the UnBound method has not been called.
Can you help me to understand where is my mistake?
I suppose I have to explicity invoke session.removeAttribute("beanUtente") but when/where?
Thanks in advance for any help :)
To avoid login from multiple browser,i don't think so that HttpSessionBindingListener will work,because with new browser,it will create new session.
You will have to save the state at your backend for the particular user,let say userId for each user,may be in cache/DB,once he login, and remove it from cache/DB once he log out,
So when user login do like below.
Object user = getUser(userId);
if(user != null){
// user already logged in,just return custom message
}
else{
// Allow user to login
}
Similary on logout remove the userId from cache/user.
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.mvcMatchers("/").permitAll()
.and().sessionManagement()
.maximumSessions(1);
}

Categories