Is using the ModelAndView in this manner "thread-safe"? The UserToken bean passed on the constructor is session-scoped and proxied, so each user should be accessing their own token, right? Or is using the same ModelAndView for all requests overwriting the UserToken each time for every user, thus possibly causing user A to see user B's token?
#Controller
public class ViewController {
private final UserToken userToken;
private final ModelAndView mav;
#Value("${redirect.url}")
String redirectUrl;
#Autowired
public ViewController(UserToken userToken) {
this.userToken = userToken;
this.mav = new ModelAndView();
}
#RequestMapping("/")
public ModelAndView defaultView() {
return getModelAndView("home");
}
#RequestMapping("/entryPoint")
public ModelAndView accessDenied(#RequestParam(required=false) String token) {
userToken.deserialize(token);
mav.addObject("userToken", userToken);
return getModelAndView("redirect:/");
}
/**
* Handle redirect if the userToken is invalid
* #param viewName The view to map
* #return the ModelAndView
*/
private ModelAndView getModelAndView(String viewName) {
if (userToken.isValid()) {
mav.setViewName(viewName);
} else {
mav.setViewName("redirect:" + redirectUrl);
}
return mav;
}
}
Not even sure how to test for thread-safety in this scenario, so any insight would be appreciated (techniques, tools, etc.).
Related
I have home page which contains features like city,country,states, user details and some pie charts and diagrams representing the data anlysis of the user.I am new to spring mvc I am in a confusion whether I should load the data of home page in one go or I should have onclick calls which would call to my controllers and load the data.
Example:
MyController Class
public class HomeController {
#Autowired
private EngagaementService engagaementService;
#Autowired
private EmployeeService employeeService;
#Autowired
private CgOfficeDetailsService cgOfficeDetailsService;
#RequestMapping("/")
public ModelAndView handleRequest() {
List<EmployeeInfo> listEmployees = employeeService.listEmployeeInfos();
ModelAndView model = new ModelAndView("index");
model.addObject("emp", listEmployees);
model.addObject("empCount", listEmployees.size());
return model;
}
#GetMapping("/getCity")
public ModelAndView getBU(HttpServletRequest request) {
String country = "India";
List<String> buList = employeeService.getCityName(country);
ModelAndView model = new ModelAndView("index");
model.addObject("buList", buList);
return model;
}
#GetMapping("/getState")
public ModelAndView getState(HttpServletRequest request) {
String country = "UP";
List<String> buList = employeeService.getState(country);
ModelAndView model = new ModelAndView("index");
model.addObject("buList", buList);
return model;
}
#GetMapping("/getUsers")
public ModelAndView getUsers(HttpServletRequest request) {
String country = "UP";
List<String> buList = employeeService.getState(country);
ModelAndView model = new ModelAndView("index");
model.addObject("buList", buList);
return model;
}
It's a question of permissions. I'd like to design my app architecture so that all the data that is accessible with current / particular authorization level (so to speak) is available on demand.
And if so, (I assume that's your case, since I don't see any further authentication in your code) it's only the question of your view design (frontend design).
I am writing a web service with an authorization and registration form. There are two types of users: regular and administrator. There is a controller that sends to the admin page at a given URL:
#Controller
public class ViewPageController {
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String sendAdminPage(){
return "AdminPage";
}
}
But ordinary users can also access this page. It is necessary that only those who logged in as an administrator get to the admin page. There are options for how this can be organized? Maybe save the logged in user in the session? (Preferably without Spring Security)
the easy way define a Aspect and A annotation.some code like this
#Inherited
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface Authorize {
//
String[] value() default {};
}
AuthorizationAspect.java
#Slf4j
#Aspect
#Component
#RequiredArgsConstructor
public class AuthorizationAspect {
private final AuthorizationService authorizationService;
private final CacheUtil cacheUtil;
private static final String PRE = "AUTH";
#Before("#annotation(com.jin.learn.config.security.Authorize)")
public void checkPermission(JoinPoint joinPoint) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Long accountId = JWTUtil.getUserIdFromRequest(request);
Set<String> authorization = cacheUtil.getAllSet(PRE + accountId);
if(authorization==null){
authorization = authorizationService.findByAccountId(accountId);
cacheUtil.save(PRE + accountId, authorization);
}
Authorize authorize = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(Authorize.class);
String[] needAuthorization = authorize.value();
if (needAuthorization.length == 0) return;
if (authorization!=null && !authorization.isEmpty()) {
if (!authorization.containsAll(Arrays.asList(needAuthorization))){
throw new SystemException(ExceptionCode.NO_PERMISSION);
}
} else {
throw new SystemException(ExceptionCode.NO_PERMISSION);
}
}
}
use like this
#Authorize(value="needRight")
#RequestMapping(value = "/admin", method = RequestMethod.GET)
public String sendAdminPage(){
return "AdminPage";
}
besides,there are some security framework shiro and spring-security
I have following spring controller code and want to return not found status if user is not found in database, how to do it?
#Controller
public class UserController {
#RequestMapping(value = "/user?${id}", method = RequestMethod.GET)
public #ResponseBody User getUser(#PathVariable Long id) {
....
}
}
JDK8 approach:
#RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public ResponseEntity<User> getUser(#PathVariable Long id) {
return Optional
.ofNullable( userRepository.findOne(id) )
.map( user -> ResponseEntity.ok().body(user) ) //200 OK
.orElseGet( () -> ResponseEntity.notFound().build() ); //404 Not found
}
Change your handler method to have a return type of ResponseEntity. You can then return appropriately
#RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public ResponseEntity<User> getUser(#PathVariable Long id) {
User user = ...;
if (user != null) {
return new ResponseEntity<User>(user, HttpStatus.OK);
}
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Spring will use the same HttpMessageConverter objects to convert the User object as it does with #ResponseBody, except now you have more control over the status code and headers you want to return in the response.
With the latest update you can just use
return ResponseEntity.of(Optional<user>);
The rest is handled by below code
/**
* A shortcut for creating a {#code ResponseEntity} with the given body
* and the {#linkplain HttpStatus#OK OK} status, or an empty body and a
* {#linkplain HttpStatus#NOT_FOUND NOT FOUND} status in case of a
* {#linkplain Optional#empty()} parameter.
* #return the created {#code ResponseEntity}
* #since 5.1
*/
public static <T> ResponseEntity<T> of(Optional<T> body) {
Assert.notNull(body, "Body must not be null");
return body.map(ResponseEntity::ok).orElse(notFound().build());
}
public static ResponseEntity of(Optional body)
A shortcut for creating a ResponseEntity with the given body and the OK status, or an empty body and a NOT FOUND status in case of an Optional.empty() parameter.
#GetMapping(value = "/user/{id}")
public ResponseEntity<User> getUser(#PathVariable final Long id) {
return ResponseEntity.of(userRepository.findOne(id)));
}
public Optional<User> findOne(final Long id) {
MapSqlParameterSource paramSource = new MapSqlParameterSource().addValue("id", id);
try {
return Optional.of(namedParameterJdbcTemplate.queryForObject(SELECT_USER_BY_ID, paramSource, new UserMapper()));
} catch (DataAccessException dae) {
return Optional.empty();
}
}
it could be shorter using Method Reference operator ::
#RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public ResponseEntity<User> getUser(#PathVariable Long id) {
return Optional.ofNullable(userRepository.findOne(id))
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
Need use ResponseEntity or #ResponseStatus, or with "extends RuntimeException"
#DeleteMapping(value = "")
public ResponseEntity<Employee> deleteEmployeeById(#RequestBody Employee employee) {
Employee tmp = employeeService.deleteEmployeeById(employee);
return new ResponseEntity<>(tmp, Objects.nonNull(tmp) ? HttpStatus.OK : HttpStatus.NOT_FOUND);
}
or
#ResponseStatus(value=HttpStatus.NOT_FOUND, reason="was Not Found")
I'm using the following code to bind the users to the model [to be used in the view/jsp]:
#ModelAttribute("users")
public Collection<User> populateUsers() {
return userService.findAllUsers();
}
But sometimes I just need to load few users with a particular Role, which I'm trying by using the following code:
int role = 2; //this is being set in a Controller within a method #RequestMapping(method = RequestMethod.GET) public String list(
#ModelAttribute("users")
public Collection<User> populateUsers() {
if(role == 2)
return userService.findAllUsersByRole(role);
else
return userService.findAllUsers();
}
but the populateUsers is always called at the start of the controller, before the role is set in list method, Could you please help me on how to set the users [something like late binding]
Regards
-- adding code
#Controller
#RequestMapping("/users")
public class UserController {
#Autowired
UserService userService;
#RequestMapping(method = RequestMethod.POST)
public String create(#Valid User user, BindingResult bindingResult,
Model uiModel, HttpServletRequest httpServletRequest) {
if (bindingResult.hasErrors()) {
uiModel.addAttribute("user", user);
addDateTimeFormatPatterns(uiModel);
return "users/create";
}
uiModel.asMap().clear();
userService.saveUser(user);
return "redirect:/users/"
+ encodeUrlPathSegment(user.getId().toString(),
httpServletRequest);
}
#RequestMapping(params = "form", method = RequestMethod.GET)
public String createForm(Model uiModel) {
uiModel.addAttribute("user", new User());
addDateTimeFormatPatterns(uiModel);
return "users/create";
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String show(#PathVariable("id") Long id, Model uiModel) {
addDateTimeFormatPatterns(uiModel);
uiModel.addAttribute("user", userService.findUser(id));
return "users/show";
}
#RequestMapping(value = "/{id}", params = "form", method = RequestMethod.GET)
public String updateForm(#PathVariable("id") Long id, Model uiModel) {
uiModel.addAttribute("user", userService.findUser(id));
addDateTimeFormatPatterns(uiModel);
return "users/update";
}
#ModelAttribute("users")
public Collection<User> populateUsers() {
return userService.findAllUsers();
}
#ModelAttribute("userroles")
public Collection<UserRole> populateUserRoles() {
return Arrays.asList(UserRole.class.getEnumConstants());
}
void addDateTimeFormatPatterns(Model uiModel) {
uiModel.addAttribute(
"user_modified_date_format",
DateTimeFormat.patternForStyle("M-",
LocaleContextHolder.getLocale()));
}
}
#PathVariable("id") Long id is the ID I require in populateUsers, hope it is clear.
If role is in the current request, this method binding role to variable role.
#ModelAttribute("users")
public Collection<User> populateUsers(#RequestParam(required=false) Integer role) {
if(role != null && role == 2)
return userService.findAllUsersByRole(role);
else
return userService.findAllUsers();
}
Setting the model attribute in the required method has solved my issue:
model.addAttribute("users", return userService.findAllUsersByRole(role));
Thanks!
This is my code:
#Controller
#RequestMapping("loginform.htm")
public class LoginController {
#RequestMapping(method = RequestMethod.GET)
public String showForm(Map<String, LoginForm> model) {
LoginForm loginForm = new LoginForm();
model.put("loginForm", loginForm);
return "loginform";
}
#RequestMapping(method = RequestMethod.POST)
public String processForm(#Valid LoginForm loginForm, BindingResult result,
Map<String, LoginForm> model) {
String userName = "UserName";
String password = "password";
if (result.hasErrors()) {
return "loginform";
}
loginForm = (LoginForm) model.get("loginForm");
if (!loginForm.getUserName().equals(userName)
|| !loginForm.getPassword().equals(password)) {
return "loginform";
}
model.put("loginForm", loginForm);
return "success";
}
}
I use this to validate form when user input username and password. But the question is when validate success, I want to add user information to session in this page. Please tell me how I can do that, I tried to add function
public ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse response)
but it show nothing. Do you have any idea? Thanks!
In general: instead of implementing security stuff by your self you should use Spring Security.
To access sessions in Spring you have tree different ways:
Work with the Http Session direcly (add the parameter HttpSession session to your controller method)
#SessionAttributes - to access an specific field of you session
Attaches beans to the session (session scoped beans)