I am using Spring-Security on top of a Spring-MVC application. I have my own implementation of UserDAO, userDetailsService. I am trying to authenticate by checking from database(ofcourse). I reach till the login page, everything seems to be working fine, but when I login, I get an error :
ERROR:
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter - An internal error occurred while trying to authenticate the user.
org.springframework.security.authentication.InternalAuthenticationServiceException: Provided id of the wrong type for class com.WirTauschen.model.User. Expected: class java.lang.Integer, got class java.lang.String
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:110)
I do not know where am I retrieving or working with user's id. I am posting the code below.
LoginService :
#Service("userDetailsService")
public class LoginServiceImpl implements UserDetailsService{
#Autowired private UserDao userDao;
#Autowired private Assembler assembler;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserDetails userDetails = null;
User user = userDao.findByName(username);
if(user == null) { throw new UsernameNotFoundException("Wrong username or password");} //Never specify which one was it exactly
return assembler.buildUserFromUserEntity(user);
}
}
Assembler
#Service("assembler")
public class Assembler {
#Transactional(readOnly = true)
User buildUserFromUserEntity(com.WirTauschen.model.User userEntity){
String username = userEntity.getUsername();
String password = userEntity.getPassword();
// int id = userEntity.getId();
boolean enabled = userEntity.isActive();
boolean accountNonExpired = userEntity.isAccountNonExpired();
boolean credentialsNonExpired = userEntity.isCredentialsNonExpired();
boolean accountNonLocked = userEntity.isAccountNonLocked();
Collection<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
User user1 = new User(username,password,enabled,accountNonExpired,credentialsNonExpired,accountNonLocked,authorities);
return user1;
}
}
User :
#Entity
#Table(name="registration")
public class User implements UserDetails{
private static final GrantedAuthority USER_AUTH = new SimpleGrantedAuthority("ROLE_USER");
#Id
#Column(name="id")
private String id=UUID.randomUUID().toString();
// #GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "my_entity_seq_gen")
//#SequenceGenerator(name ="my_entity_seq_gen", sequenceName = "MY_ENTITY_SEQ")
#OneToMany
private Set<ProductBasic> productBasic;
#Column(name = "email")
private String email;
#Column(name = "username")
private String Username;
#Column(name = "displayname")
private String DisplayName;
#Column(name = "password")
private String password;
#Column(name = "companyname")
private String CompanyName;
#Column(name = "firstname")
private String FirstName;
#Column(name = "middlename")
private String MiddleName;
private String role="ROLE_USER";
#Transient
private final String PERMISSION_PREFIX = "ROLE_USER";
#Transient
private List<GrantedAuthority> authorities;
public User() {
this.authorities = new ArrayList<GrantedAuthority>();
authorities.add(USER_AUTH);
}
public User(String Username, String password, String Role){
this.Username = Username;
this.password = password;
this.role = Role;
if((role == null) || role.isEmpty()){ role = "ROLE_USER";}
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
Update sequence code
#Id
#Column(name = "sortcanvasid")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sortcanvas_gen")
#SequenceGenerator(name = "sortcanvas_gen", sequenceName = "sortcanvas_seq")
private int sortCanvasId;
Seems that your User class not implementing UserDetails Morever it might not be having proper return type of the getID method
Related
#Convert(converter = MsisdnEncryptor.class,disableConversion=true)
I have used this converter in my spring boot entity class.
package com.example.demo.entity;
This is the entity class:
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Table(name="employee")
public class Employee {
#Id
#Column(name = "emp_id")
#SequenceGenerator(
name = "employee_sequence",
sequenceName = "employee_sequence",
allocationSize = 1
)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "employee_sequence")
private Long EmpId;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "mobile_number")
**#Convert(converter = MsisdnEncryptor.class,disableConversion=true)**
**here I want to put values from application.properties in disableConversion**
private String mobileNumber;
#Column(name= "date_time")
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd-MM-yyyy HH:mm:ss", timezone="GMT+5:30")
private Timestamp dateTime;
}
This is the Encryptor class:
#Component
public class MsisdnEncryptor implements AttributeConverter<String, String> {
private static final Logger logger = LoggerFactory.getLogger(MsisdnEncryptor.class);
#Value("${is.enabled:true}")
private boolean isEnabled;
private static final String AES = "AES";
private static final String SECRET = "secret-key-12345";
private final Key key;
private final Cipher cipher;
public MsisdnEncryptor() throws Exception {
key = new SecretKeySpec(SECRET.getBytes(), AES);
cipher = Cipher.getInstance(AES);
}
#Override
public String convertToDatabaseColumn(String attribute) {
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
return Base64.getEncoder().encodeToString(cipher.doFinal(attribute.getBytes()));
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) {
throw new IllegalStateException(e);
}
}
#Override
public String convertToEntityAttribute(String dbData) {
try {
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(Base64.getDecoder().decode(dbData)));
} catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
throw new IllegalStateException(e);
}
}
}
I know we can use #Value for taking the values , but it doesn't work here as it says attribute value must be constant.
Or if there is any other way of doing the encryption and enabling/disabling it from config , I would be more than happy to go through it.
I'm using a Thymeleaf HTML registration form and simple save/update method to save/update a 'dish' object to a mySQL database. Restaurant Id is a foreign key for the 'dish' but using the below methods it saves as 'null',
I would like to make it so that the Restaurant id of the currently logged in restaurant owner saves automatically when they add a dish.
Is there an uncomplicated way to do this? The closest tutorial I've found on Youtube involves using JSON requests in Postman and I've had issue adapting that to a HTML registration form in the past.
I'm quite new to all of this so any help would be very much appreciated!
See Dish class:
package com.bron.demoJPA.appuser;
#Entity
#Data
#AllArgsConstructor
#NoArgsConstructor
#Builder
#ToString(exclude = "reqlist")
public class Dish {
#Id
#SequenceGenerator(name = "dish_sequence", sequenceName = "dish_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "dish_sequence")
#Column(name = "dish_Id")
private Long dishId;
#Column(name = "dish_name")
private String dname;
#Column(name = "dish_description")
private String description;
#Column(name = "dish_price")
private double price;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "Rest_ID", referencedColumnName = "Rest_ID")
private AppUser app;
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "dish_requirment_mapping", joinColumns = #JoinColumn(name = "dish_Id", referencedColumnName = "dish_Id"), inverseJoinColumns = #JoinColumn(name = "Require_ID", referencedColumnName = "Require_ID"))
private List<Requirments> reqlist;
public void addRequirments(Requirments req) {
if (reqlist == null)
reqlist = new ArrayList<>();
reqlist.add(req);
}
}
See AppUser(restaurant owner) Class
#Column(name = "Rest_Password")
private String password;
#Column(name = "Rest_Email_Address")
private String email;
#Enumerated(EnumType.STRING)
private AppUserRole appUserRole;
private Boolean locked = false;
// don't enable user until email verification
private Boolean enabled = false;
public AppUser(String restname, String email, String pass, AppUserRole app) {
this.restaurantName = restname;
this.email = email;
this.password = pass;
this.appUserRole = app;
}
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(appUserRole.name());
return Collections.singletonList(authority);
}
#Override
public String getUsername() {
return email;
}
#Override
public String getPassword() {
return password;
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return !locked;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return enabled;
}
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "openingHourID", referencedColumnName = "OpeningHour_ID")
private OpeningHour opening;
}
See Controller class:
package com.bron.demoJPA.conroller;
#Controller
public class DishController {
//display list of employees
#Autowired
private DishService dishService;
#GetMapping("/dish")
public String viewHomePage(Model model) {
model.addAttribute("listDish", dishService.getAllDish());
return "index";
}
#GetMapping("/showNewDishForm")
public String showNewDishForm(Model model) {
// Create model attribute to bind form data
Dish dish = new Dish();
model.addAttribute("dish", dish);
return "new_dish";
}
#PostMapping("/saveDish")
public String saveDish(#ModelAttribute("dish") Dish dish) {
// save dish to database
dishService.saveDish(dish);
return "redirect:/dish";
}
#GetMapping("/showFormForUpdate/{dishId}")
public String showFormForUpdate(#PathVariable(value = "dishId") long dishId, Model model) {
// get dish from service
Dish dish = dishService.getDishByDishId(dishId);
// set dish as model to pre-populate the form data
model.addAttribute("dish", dish);
return "update_dish";
}
}
See Service implementation
package com.bron.demoJPA.service;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.bron.demoJPA.appuser.Dish;
import com.bron.demoJPA.repository.DishRepository;
#Service
public class DishServiceImpl implements DishService {
#Autowired
private DishRepository dishRepository;
#Override
public List<Dish> getAllDish() {
return dishRepository.findAll();
}
#Override
public void saveDish(Dish dish) {
this.dishRepository.save(dish);
}
#Override
public Dish getDishByDishId(long dishId) {
Optional<Dish> optional = dishRepository.findById(dishId);
Dish dish = null;
if (optional.isPresent()) {
dish = optional.get();
} else {
throw new RuntimeException("Dish not found for: " + dishId);
}
return dish;
}
}
See Service class
public interface DishService {
List<Dish> getAllDish();
void saveDish(Dish dish);
Dish getDishByDishId(long dishId);
}
Can you make sure Dish's "app" attribute is being set correctly before trying to save it?
If it's null or it's a brand new instance of AppUser class it makes sense that when trying to match and persist it ends up on null.
Greetings!
I am testing web application using Junit 5, every thing working fine except
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
line in controller class while running test case above code of line return object of
org.springframework.security.core.userdetails.User instead of MyUserDetails (While running application in spring boot return object is MyUserDetails and code works well)
My Test case class
#ExtendWith(SpringExtension.class)
#WebMvcTest(VendorController.class)
#AutoConfigureMockMvc
class VendorControllerTest {
#Autowired
private MockMvc mvc;
#Test
void test() throws Exception {
mvc.perform(MockMvcRequestBuilders.post("/vendor/user/getVendorList").with(csrf().asHeader())
.param("iDisplayStart","0")
.param("iDisplayLength", "10")
.param("iSortCol_0", "0")
.param("sSortDir_0", "asc")
.param("sSearch","")
.with(user("username").password("password").roles("2"))).andExpect(status().isOk());
}
}
What code I am missing so that it is return org.springframework.security.core.userdetails.User object while running test case using Junit 5 but working properly if I run project through spring boot.
Adding UserDetailsService implementation code
#Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
#Autowired
private UserSvc userSvc;
#Transactional(readOnly=true)
#Override
public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException {
com.esociety.entity.UserEntity userEntity = userSvc.findByUserName(username);
List<GrantedAuthority> authorities = buildUserAuthority(userEntity.getUserType());
return buildUserForAuthentication(userEntity, authorities);
}
private User buildUserForAuthentication(com.esociety.entity.UserEntity userEntity, List<GrantedAuthority> authorities) {
MyUserDetails myUserDetails = new MyUserDetails (userEntity.getUsername(), userEntity.getPassword(), userEntity.isEnabled(), userEntity.isAccountNonExpired(), userEntity.isCredentialsNonExpired(),userEntity.isAccountNonLocked(),authorities,userEntity.getUserId(),userEntity.getSocietyId(),userEntity.getUserType(),userEntity.getFirstName(),userEntity.getMiddleName(),userEntity.getLastName());
return myUserDetails;
}
private List<GrantedAuthority> buildUserAuthority(String userType) {
Set<GrantedAuthority> setAuths = new HashSet<GrantedAuthority>();
// Build user's authorities
setAuths.add(new SimpleGrantedAuthority(userType));
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(setAuths);
return Result;
}
}
MyUserDetails class
public class MyUserDetails extends User {
public MyUserDetails(String username, String password, boolean enabled,
boolean accountNonExpired, boolean credentialsNonExpired,
boolean accountNonLocked,
Collection<? extends GrantedAuthority> authorities, Long userId,
Long societyId, String userType, String firstName,
String middleName, String lastName) {
super(username, password, enabled, accountNonExpired,
credentialsNonExpired, accountNonLocked, authorities);
this.userId = userId;
this.societyId = societyId;
this.userType = userType;
this.firstName = firstName;
this.middleName = middleName;
this.lastName = lastName;
}
// getter setter goes here
}
The problem is you are using SecurityMockMvcRequestPostProcessors.user(String username) RequestPostProcessor which will create and return a org.springframework.security.core.userdetails.User which is an implementation of UserDetails but not the MyUserDetails that you want.
Instead you can use SecurityMockMvcRequestPostProcessors.user(UserDetails user) which allows to submit your own implementation of UserDetails, in this case MyUserDetails that you want.
Change your test as following:
#Test
void test() throws Exception {
mvc.perform(MockMvcRequestBuilders.post("/vendor/user/getVendorList").with(csrf().asHeader())
.param("iDisplayStart","0")
.param("iDisplayLength", "10")
.param("iSortCol_0", "0")
.param("sSortDir_0", "asc")
.param("sSearch","")
.with(user(new MyUserDetails ("username", "password", true, true, true,true, Arrays.asList(new SimpleGrantedAuthority("2")),1,1,USER_TYPE,"FirstName","MiddleName","LastName")))).andExpect(status().isOk());
}
Reference Spring Security Source code
I am attempting to connect to my database in a Spring MVC application. There are two tables. Users and Orders, Users has a primary key column: "userID", orders has a composite key from columns: "userID" and "orderID", where userID is a foreign key referencing the "userID" column in the Users table.
Here are my classes:
Order:
#Entity
#Table(name = "Orders")
#IdClass(OrderPK.class)
public class Order implements Serializable{
private static final Long serialVersionUID = 1L;
#EmbeddedId
private OrderPK orderPK;
//other properties
//no args and full args constructor
//getters and setters
//toString
}
OrderPK:
#Embeddable
public class OrderPK implements Serializable {
#Column(name = "orderID")
private Long orderID;
#ManyToOne
#JoinColumn(name = "userID")
private User user;
public OrderPK() {
}
public OrderPK(Long orderID, User user) {
this.orderID = orderID;
this.user = user;
}
public Long getOrderID() {
return orderID;
}
public void setOrderID(Long orderID) {
this.orderID = orderID;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof OrderPK)) return false;
OrderPK that = (OrderPK) o;
return Objects.equals(getOrderID(), that.getOrderID()) &&
Objects.equals(getUser(), that.getUser());
}
#Override
public int hashCode() {
return Objects.hash(getOrderID(), getUser());
}
}
User:
#Entity
#Table(name = "USERS")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(name="USER_SEQUENCE", sequenceName="USER_SEQUENCE")
#GeneratedValue(strategy=GenerationType.SEQUENCE,
generator="USER_SEQUENCE")
#Column(name = "userid")
private Long userId;
//other properties
//no args and full args constructor
//getters and setters
//toString
}
When I try to connect to the database I get the following exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: Unable to find properties (orderID, user) in entity annotated with #IdClass:com.ex.evemarketback.domain.Order
...
Caused by: org.hibernate.AnnotationException: Unable to find properties (orderID, user) in entity annotated with #IdClass:com.ex.evemarketback.domain.Order
Any suggestions?
As you are using the #EmbeddedId, you do not need the #IdClass annotation:
#Entity
#Table(name = "Orders")
public class Order implements Serializable{
or if you want to keep the #IdClass:
// #Embeddable - no need for that
public class OrderPK implements Serializable {
private Long orderID;
private Long userId;
...
}
entity:
#Entity
#Table(name = "Orders")
#IdClass(OrderPK.class)
public class Order implements Serializable{
#Id
#Column(name = "orderID")
private Long orderID;
#Id
#Column(name = "userId", insertable=false, updatable=false)
private Long userId;
#ManyToOne
#JoinColumn(name = "userID")
private User user;
I have the following class:
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class TclRequest implements Comparable<TclRequest> {
#PrimaryKey
private String id;
#Persistent(types = { DNSTestData.class, POP3TestData.class, PPPoETestData.class, RADIUSTestData.class }, defaultFetchGroup = "true")
#Columns({ #Column(name = "dnstestdata_fk"), #Column(name = "pop3testdata_fk"), #Column(name = "pppoetestdata_fk"), #Column(name = "radiustestdata_fk") })
private TestData testData;
public String getId() {
return id;
}
public TestData getTestData() {
return testData;
}
public void setId(String id) {
this.id = id;
}
public void setTestData(TestData testData) {
this.testData = testData;
}
}
The TestData interface looks like this:
#PersistenceCapable(detachable = "true")
public interface TestData {
#PrimaryKey
public String getId();
public void setId(String id);
}
Which is implemented by many classed including this one:
#PersistenceCapable(detachable = "true")
public class RADIUSTestData implements TestData {
#PrimaryKey
private String id;
private String password;
private String username;
public RADIUSTestData() {
}
public RADIUSTestData(String password, String username) {
super();
this.password = password;
this.username = username;
}
#Override
public String getId() {
return id;
}
#Override
public void setId(String id) {
this.id = id;
}
}
When I try to persiste the TclRequest class, after constructing it of course and using the RADIUSTestData:
//'o' is the constructed TclRequest object.
PersistenceManager pm = null;
Transaction t = null;
try {
pm = getPM();
t = pm.currentTransaction();
t.begin();
pm.makePersistent(o);
t.commit();
} catch (Exception e) {
e.printStackTrace();
if (t != null && t.isActive()) {
t.rollback();
}
} finally {
closePM(pm);
}
The interface field isn't persisted. And the column is not created in the table ! I enabled the debug mode and found 2 catchy things:
1)
-Class com.skycomm.cth.beans.ixload.radius.TestData specified to use "application identity" but no "objectid-class" was specified. Reverting to javax.jdo.identity.StringIdentity
2)
-Performing reachability on PC field "com.skycomm.cth.beans.TclRequest.testData"
-Could not find StateManager for PC object "" at field "com.skycomm.cth.beans.TclRequest.testData" - ignoring for reachability
What could this mean ?
Thanks in advance.
I have figured out how to do it. It's not very much scalable but it works for now.
These are the annotations for the interface member variable. Note that the order of declared types, columns and class names in the extension value is important to be maintaned:
#Persistent(types = { RADIUSTestData.class, POP3TestData.class, PPPoETestData.class, DNSTestData.class }, defaultFetchGroup = "true")
#Columns({ #Column(name = "radiustestdata_fk"), #Column(name = "pop3testdata_fk"), #Column(name = "pppoetestdata_fk"),
#Column(name = "dnstestdata_fk") })
#Extension(vendorName = "datanucleus", key = "implementation-classes", value = "com.skycomm.cth.tcl.beans.radius.RADIUSTestData, com.skycomm.cth.tcl.beans.pop3.POP3TestData, com.skycomm.cth.tcl.beans.pppoe.PPPoETestData, com.skycomm.cth.tcl.beans.dns.DNSTestData")
A sample class implementing one of the interfaces (Just it's "header"):
#PersistenceCapable(detachable = "true")
public class RADIUSTestData implements TestData {
So it's pretty normal here.