how to disable spring security authentication on just a few methods - java

I have spring basic authentication implemented, but there are a few urls which I don't want to be authenticated. For example, http://www.mywebsite.com/rest/signUp
How to I make this unauthenticated?
#Transactional
#RequestMapping(value = "/signUp", headers = "Accept=application/json", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public #ResponseBody String signUp(#RequestParam("user_name") String login,
#RequestParam("pass_word") String passWord,
#RequestParam("first_name") String firstName,
#RequestParam("last_name") String lastName,
#RequestParam("network_name") String networkName,
#RequestParam("email") String email) {
if(!userDAO.loginExists(login)) {
User user = new User();
user.setLogin(login);
user.setFirstName(firstName);
user.setLastName(lastName);
user.setNetworkName(networkName);
user.setEmail(email);
user.setPassword(passWord);
sessionFactory.getCurrentSession().save(user);
return json("success");
}
return json("failure");
}

<http>
<intercept-url pattern="/rest/signUp" access="IS_AUTHENTICATED_ANONYMOUSLY"/>
<!-- your other interceptor-url elements -->
</http>
Add an intercept-url pattern for your signup service which is authenticated anonymously.

Related

how to solve bad credentials spring Security?

I can't log in to my app as a user with the role admin or as a user with the role user. I always log in as a user with the role anonymous.
Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Bean
public PasswordEncoder encoder() {
return new StandardPasswordEncoder("53cr3t");
}
#Autowired
UserDetailsServiceImpl userDetailsService;
public SecurityConfig(UserDetailsServiceImpl userDetailsService) {
this.userDetailsService = userDetailsService;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Bean
public DaoAuthenticationProvider authProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
authProvider.setPasswordEncoder(encoder());
return authProvider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/").authenticated()
.antMatchers("/rentAppPage/").hasRole("ADMIN")
.antMatchers("/addVehicle").hasRole("ADMIN")
.antMatchers("/getVehicle").hasRole("ADMIN")
.antMatchers("/removeVehicle").hasRole("ADMIN")
.antMatchers("/updateVehicle").hasRole("ADMIN")
.antMatchers("/allUser").hasRole("ADMIN")
.antMatchers("/resultGet").hasRole("ADMIN")
.antMatchers("/addUser").hasRole("ADMIN")
.antMatchers("/getUser").hasRole("ADMIN")
.antMatchers("/updateUser").hasRole("ADMIN")
.antMatchers("/removeUserById").hasRole("ADMIN")
.antMatchers("/price").hasAnyAuthority("ROLE_ADMIN", "ROLE_USER")
.antMatchers("/allScooter").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/allCar").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/allMotorBike").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/allBike").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
.antMatchers("/distance").hasAnyAuthority("ROLE_USER", "ROLE_ADMIN")
.antMatchers("/user").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/rent").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/rent2").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/buy").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/buy2").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/thanks").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.antMatchers("/rentAppPage").hasAnyAuthority( "ROLE_ADMIN", "ROLE_USER")
.and()
.formLogin()
.defaultSuccessUrl("/", true)
.and()
.logout()
.logoutSuccessUrl("/");
;
http.sessionManagement()
//.expiredUrl("/sessionExpired.html")
.invalidSessionUrl("/login.html");
}
}
#Entity
#Table(name = "user")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int idUser;
private String username;
private String password;
private String name;
private String surname;
private String email;
private double latitude;
private double longitude;
private String role;
private String locationName;
}
#Slf4j
#Service
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
IUserDAO userDAO;
public UserDetailsServiceImpl(IUserDAO userDAO){
this.userDAO = userDAO;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDAO.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException("User dont exist");
}
return new MyUserDetails(user);
}
}
#Repository
public class UserDAOImpl implements IUserDAO {
#Autowired
SessionFactory sessionFactory;
public UserDAOImpl(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
#Override
public void addUser(User user){
Session session = null;
Transaction tx = null;
try{
session = this.sessionFactory.openSession();
tx = session.beginTransaction();
session.save(user);
tx.commit();
}catch (HibernateException e){
if(tx != null)tx.rollback();
}finally {
session.close();
}
}
#Override
public User findByUsername(String username) {
Session session = this.sessionFactory.openSession();
User user =(User) session.createQuery("FROM pl.edu.wszib.model.User WHERE username = :username" )
.setParameter("username", username)
.uniqueResult();
session.close();
return user;
}
}
public class MyUserDetails implements UserDetails {
private User user;
public MyUserDetails(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(user.getRole());
return Arrays.asList(authority);
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
}
2021-01-18 17:05:43.545 DEBUG 4256 --- [io-8080-exec-10] o.s.s.w.a.AnonymousAuthenticationFilter : Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken#cd98cfcc: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#0: RemoteIpAddress: 127.0.0.1; SessionId: AD82C9600EFB66CF7C6F8A1BCCEEAE0D; Granted Authorities: ROLE_ANONYMOUS'
i have databse in MySQL with two users first role admin second role user my role in database is ROLE_ADMIN,ROLE_USER i my full code is here https://github.com/Conrado1212/SpringSecurityWhyCantWorkFine
can someone explain why i cant log in to app as user with role admin or user ?
Of course, there are many questions about encoder, md5, etc. But it's your choice even it's wrong )))
I run your code, if MD5 hash is correct in DB I can login. As I understand, the question is why the application does not store the state of logged in user?
Expectation:
1st request: call method (incognito) -> login -> call method (authorized)
Xth request: call method -> Spring Security checks -> call method (authorized)
Reality:
Xth request: always equals 1st
The cause that you don't change a state of Spring Security context. You call /login endpoint, check if the User is in DB, check his password and return success response. You should create an authorization and place it in the Spring Security context.
Simple example:
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
new UsernamePasswordAuthenticationToken(username, password);
Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
String token = tokenProvider.generateToken(authentication);
return token;
And in next requests use the token. How to implement the token provider there are many examples. It can be custom or better JWT.
If you don't want tokens (RESTful), you can use sessions in requests (RESTless) but it requires additional configuration.
Looked through the code you have posted on github and none of the code you posted in the above question is the problem. You problem is how you perform your login.
login.html
<form action="/authenticate" method="post" id="login" class="input-group">
<div th:text="`enter code here`${errorMessage}" id="error-message"></div>
<input type="text" class="input-field" placeholder="Enter username" name="username" th:field="*{userModel.username}" required>
<input type="password" class="input-field" placeholder="Enter password" name="password" th:field="*{userModel.password}"required>
<input type="checkbox" class="chech-box"><span class="span1">Remember password</span>
<button type="submit" class="submit-btn">Log in</button>
</form>
Here we se that the login form posts to the endpoint /authenticate. This is NOT the standard /login endpoint that spring security sets up for you automatically.
Since you are not using the the standard i find your custom endpoint.
LoginController.java
#RequestMapping(value = "/authenticate",method = RequestMethod.POST)
public String authenticateUser(#ModelAttribute("userModel")User user,Model model){
boolean authResult = this.authenticationService.authenticationUser(user);
if(authResult){
System.out.println("zalogowano !!");
return "rentAppPage";
} else {
model.addAttribute("errorMessage","zle dane!!!");
model.addAttribute("userModel",new User());
return "login";
}
}
Here we see that you pass the user object into a custom written function called authenticationUser. So if we look in that function we find this implementation.
AuthenticationService.java
#Override
public boolean authenticationUser(User user){
User userFromDb = this.userDAO.findByUsername(user.getUsername());
return userFromDb != null && userFromDb.getPassword().equals(DigestUtils.md5Hex(user.getPassword()));
}
All you do in this function, is to fetch the user from the database. Check if the users password matches, and then return a boolean.
If the boolean is true you return the next page.
This is NOT how spring security works. All of the above code is completely faulty, and its clear that no research has been done before asking here on stack overflow. How could the server know you have called that function before? it can't.
My answer to your question, is that your implementation is completely wrong, and i highly suggest you find a getting started guide to how form login works in spring security, because explaining how spring security works, can not be done in a simple answer.
All im going to do is to link you to the official Spring security FormLogin documentation, and you should start reading there.

Spring boot how make a user role managing with jwt

I'm writing a RESTful api with spring boot.
I'm using spring boot, jersey, mongo db, swagger, spring boot security and jwt.
I have written the models, the repositories for the requests to the DB. Now I have integrated the Security and jwt token.
Now I need to discretize the role of the users, because a user cant call a route that need an admin priviledges.
I have a route for login, it's return a token. This is the code of my SecurityConfig
...
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter{
#Autowired
UserRepository userRepository;
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable().authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/swagger.json").permitAll()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.antMatchers("/api/*").authenticated()
.and()
.addFilterBefore(new JWTLoginFilter("/login", authenticationManager(), userRepository),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JWTAuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class);
}
}
I written the JWTLoginFilter that return me the token when user makes login
...
#Override
public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException, IOException, ServletException {
Credential creds = new ObjectMapper().readValue(req.getInputStream(), Credential.class);
User user = userRepository.login(creds);
if (user == null)
throw new BadCredentialsException("");
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
creds.getUsername(),
creds.getPassword()
);
return token;
}
...
I want insert this on my endpoint class on method
#PreAuthorize("hasRole('ROLE_ADMIN')")
this is a part of an endpoint
....
#Component
#Path("story")
#Api(value = "Story", produces = "application/json")
public class StoryEndpoint {
private static final Logger LOGGER = LoggerFactory.getLogger(StoryEndpoint.class);
#Autowired
StoryRepository storyRepository;
#GET
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
#PreAuthorize("hasRole('ROLE_ADMIN')") <--- I want insert here
#ApiOperation(value = "Get All Story", response = Story.class)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "hello resource found"),
#ApiResponse(code = 404, message = "Given admin user not found")
})
public Response getAllStory(){
Iterable<Story> stories = storyRepository.findAll();
LOGGER.info("getAllStory");
return (stories!=null) ? Response.ok(stories).build() : Response.ok(ResponseErrorGenerator.generate(Response.Status.NOT_FOUND)).status(Response.Status.NOT_FOUND).build();
}
....
How I can make a mechanism for assign to user the role and how i can pass the role in token and discretize on route the role of user?
You need to store user roles inside JWT token as additional claims, extract them after token validation and pass as 'authorities' for principal:
Collection<? extends GrantedAuthority> authorities
= Arrays.asList(claims.get(AUTHORITIES_KEY).toString().split(",")).stream()
.map(authority -> new SimpleGrantedAuthority(authority))
.collect(Collectors.toList());
User principal = new User(claims.getSubject(), "",
authorities);
UsernamePasswordAuthenticationToken t
= new UsernamePasswordAuthenticationToken(principal, "", authorities);
First need to add the roles inside the JWT. For that you can add as Claim in the JWT Generator class.
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
Set<String> Userroles = new HashSet<>();
User user = userRepository.findByUsername(userDetails.getUsername());
for(Role role:user.getRoles()){
Userroles.add(role.getName());
}
claims.put("Roles",Userroles.toArray());
return createToken(claims, userDetails.getUsername());
}
private String createToken(Map<String, Object> claims, String subject) {
return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
}
In the user model class need to include the roles in Set or any other data structure.
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(name = "USER_ROLES", joinColumns = {
#JoinColumn(name = "USER_ID") }, inverseJoinColumns = {
#JoinColumn(name = "ROLE_ID") })
private Set<Role> roles;
In the Repository need to have a method like below.
User findByUsername(String username);
Please check this Github Repo(https://github.com/Senthuran100/SpringBoot_JWT) for your reference.
you should add role into Token and for example you can refer following link:-
http://www.svlada.com/jwt-token-authentication-with-spring-boot/

Spring Boot - Save User between Controller Methods

What's the best approach to avoid repeating the same userService DB lookup over and over again in my controller methods?
I'm using Spring Boot 1.5.2 with spring-boot-starter-security and spring-boot-starter-thymeleaf for templating.
I tried adding an instance variable for SecurityContextHolder.getContext().getAuthentication() but it gave me a NullPointerException.
#Controller
public class DashboardController {
#Autowired
private UserService userService;
#Value("${product.name}")
private String productName;
#RequestMapping(value="/dashboard", method = RequestMethod.GET)
public ModelAndView home() {
ModelAndView modelAndView = new ModelAndView();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findUserByEmail(auth.getName());
modelAndView.addObject("email", user.getEmail());
modelAndView.setViewName("dashboard");
return modelAndView;
}
#RequestMapping(value="/dashboard/faq", method = RequestMethod.GET)
public ModelAndView faq(){
ModelAndView modelAndView = new ModelAndView();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findUserByEmail(auth.getName());
modelAndView.addObject("email", user.getEmail());
modelAndView.addObject("productname", productName);
modelAndView.setViewName("faq");
return modelAndView;
}
If you want to get at the user that is stored in the session, you can use this annotation:
#RequestMapping("/me")
public User me(#AuthenticationPrincipal User user) {
return user;
}
If you then want the user to always be available in thymeleaf I would use a #ControllerAdvice
#ControllerAdvice(annotations = Controller.class)
public class GlobalVariablesControllerAdvice {
#ModelAttribute("user")
public User user() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = null;
// get user from authentication, but make sure to check for nulls
return user;
}
}

Spring MVC user data update issue

I’ve an issue with update of user data in Spring MVC app.
So, I have user and I have FORM filled with data from JSP. Now data from FORM overrides all fields of user data with null, except entered in jsp.
In another case – user’s data overrides form’s data.
Help me, please, to do it correctly. I’ve tried a lot of variants but nothing works.
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public ModelAndView updateView(#ModelAttribute(value = "updateForm")
HttpSession session) {
User user = (User) session.getAttribute("user");
UserForm updateForm = new UserForm();
updateForm.setUser(user);
return new ModelAndView("profileupdate", "updateForm", updateForm);
}
#RequestMapping(method = RequestMethod.POST)
public String updateUserProcess(#ModelAttribute(value = "updateForm")
UserForm updateForm,
BindingResult result, Model model,
HttpSession session) {
User user = (User) session.getAttribute("user");
model.addAttribute("updateForm", updateForm);
if (result.hasErrors()) {
return "profileupdate";
}
if (!updatingUser(updateForm, model, user))
model.addAttribute("errorMsg", "Login or Email is already in use!");
return "profileupdate";
}
return "updated";
}
private boolean updatingUser(UserForm updateForm, Model model, User user) {
fillForm(updateForm, user);
user = updateForm.getUser();
//Another case
//user = updateForm.getUser();
//fillForm(updateForm, user);
return userService.updateUser(user);
}
private void fillForm(UserForm updateForm, User user) {
updateForm.setUserId(user.getUserId());
updateForm.setLogin(user.getLogin());
updateForm.setPassword(user.getPassword());
updateForm.setEmail(user.getEmail());
}
}
** UserForm class**
public class UserForm {
private Integer userId;
private String login;
private String name;
private String password;
private String email;
public UserForm() {
}
public User getUser() {
User user = new User();
user.setUserId(userId);
user.setLogin(login);
user.setPassword(password);
user.setName(name);
user.setEmail(email);
return user;
}
public void setUser(User user) {
this.userId = user.getUserId();
this.login = user.getLogin();
this.password = user.getPassword();
this.name = user.getName();
this.email = user.getEmail();
………………………….
getters and setters
}
This is my DAO and Service
#Override
public boolean updateUser(User user) {
return userDao.updateUser(user);
}
#Override
#Transactional
public boolean updateUser(User user) {
if (isUserExists(user)) {
return false;
}
currentSession().update(user);
return true;
}
Updade.jsp
<sf:form name="login"
method="POST"
action="${app}/edit"
modelAttribute="updateForm"
enctype="application/x-www-form-urlencoded">
<label for="login">Login:</label><br>
<input name="login" id="login" type="text" value=""/> <br>
<sf:errors path="login" cssClass="error"/><br>
<br><label for="password">Password:</label>
<br><input name="password" id="password" type="password" value=""/>
<br>
<sf:errors path="password" cssClass="error"/><br>
<br> <input type="submit" name="submit" value="Update"/>
</sf:form>
It would be very hard for spring or hibernate to guess what values are null because user wants them to be null and what are null because they do not have to be touched. You as the progammer have to supply a fully completed object.
There are two common ways to do that :
you suppose null fields should be left untouched and modify fillform accordingly :
if (updateForm.getUserId().isEmpty()) { updateForm.setUserId(user.getUserId()); }
...
you prefil your form with current User value in the get that precedes the post (more common unless you need a post without the get part before)
EDIT
To prefill the form (the jsp part seems to be fine) your controller should put a filled UserFormin the model in the GET method.
#RequestMapping(method = RequestMethod.GET)
public String updateView(#ModelAttribute(value = "updateForm")
UserForm updateForm,
HttpSession session) {
User user = (User) session.getAttribute("user");
updateForm.setUser(user);
return "profileupdate";
}
with updateForm being implicitely in model due to the #ModelAttribute annotation, or
#RequestMapping(method = RequestMethod.GET)
public ModelAndView updateView(HttpSession session) {
updateForm = new UserForm();
User user = (User) session.getAttribute("user");
updateForm.setUser(user);
return new ModelAndView("profileupdate", "updateForm", updateForm);
}
I also removed the value="/edit" because it is not on updateUserProcess and I assumed the "/edit" was allready established on controller.
Well, main problem was in the path on JSP. Instead of handle request through controller I've set just a link to the page. So, advice - be careful and attentive with mappings.
WRONG VERSION OF LINK
<form name="redaction"
action="${pageContext.request.contextPath}/updatepage.jsp"
method="GET"
enctype="application/x-www-form-urlencoded">
<input type="submit" name="submit" value="Redaction"/>
</form>
RIGHT VERSION OF LINK
<form name="redaction"
action="${pageContext.request.contextPath}/edit"
method="GET"
enctype="application/x-www-form-urlencoded">
<input type="submit" name="submit" value="Redaction"/>
</form>
And controller
#RequestMapping(value = "/edit", method = RequestMethod.GET)
public String updateView(UserForm userForm,
HttpSession session,
ModelMap model) {
User user = (User) session.getAttribute("user");
userForm.setUser(user);
model.addAttribute("userForm", userForm);
return "profileupdate";
}
#RequestMapping(value = "/edit.do", method = RequestMethod.POST)
public String updateUserProcess(#ModelAttribute(value = "userForm")
UserForm userForm,
BindingResult result, Model model,
HttpSession session) {
User user = (User) session.getAttribute("user");
session.getAttribute("userForm");
model.addAttribute("userForm", userForm);
userValidator.validate(userForm, result);
if (result.hasErrors()) {
logger.error("Validation error");
return "profileupdate";
}
if (!updatingUser(userForm, model, user)) {
logger.error("User update error!");
logger.error("Login or Email is already in use!");
model.addAttribute("errorMsg", "Login or Email is already in use!");
return "profileupdate";
}
logger.info("User updated!");
return "newuserprofile";
}
Have you checked the values of user.getUserID,user.getLogin(),user.getPassword(),user.getEmail()
in the following segment of code? Is it null or the data you recieved in the model object User?
updateForm.setUserId(user.getUserId());
updateForm.setLogin(user.getLogin());
updateForm.setPassword(user.getPassword());
updateForm.setEmail(user.getEmail());
Please post the code for userService.updateUser(user) so that we can understand more.

Spring MVC routing to wrong controller

I have a web application in SpringMVC, After login I want to point it to "/" but its always pointing me to /home.
<security:form-login
login-page="/home/login"
default-target-url="/"
always-use-default-target="true"
authentication-failure-url="/auth/failed"/>
here is homecontroller.java
#Controller
#RequestMapping(value = "/")
#SessionAttributes({"loginModel"})
public class HomeController {
#Autowired
private LoginModelService loginModelService;
#RequestMapping(method = RequestMethod.GET)
public String loadHome(Model model, Principal principal) {
model.addAttribute("loginModel", loginModelService.getLoginModelByUserLoginName(principal.getName()));
return "index";
}
#RequestMapping(method = RequestMethod.GET, value = "/home")
public String showHome(Model model, Principal principal) {
model.addAttribute("loginModel", loginModelService.getLoginModelByUserLoginName(principal.getName()));
return "system/home";
}
}
After login showHome method is being called instead of loadHome

Categories