I'm trying to make a simple forum just to get the hang of the Spring Security and MVC frameworks.
For simplicity's sake, let's I have a JSP to view a forum post, which looks like the following:
<body>
...
Title: ${forumPost.title} <br>
Author: ${forumPost.author.name} <br>
Message: {forumPost.message} <br>
<security:authorize ifAnyGranted="ROLE_ADMIN">
Edit: Edit
</security:authorize>
...
</body>
My problem is: not only should an Administrator be able to edit this post, but the original author should be able to as well. Therefore, I only want ROLE_ADMIN and the original author to be able to see the Edit link. However I'm not sure how to filter by user with the security:authorize tag, or if I'll need to go about this a different way.
Any suggestions would be much appreciated. Thanks!
Assuming that you have a controller that sits behind this page, I would simply add a canEditPost field to the ModelAndView that looks something like (semi-pseudocode):
private boolean isAdmin() {
Authentication currentAuthObj = SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> authorities = Arrays.asList(currentAuthObj.getAuthorites());
for (GrantedAuthority auth : authorities) {
if ("ROLE_ADMIN".equals(auth.getAuthority())) {
return true;
}
}
return false;
}
boolean currentUserIsAuthor = ...;
modelAndView.addObject("canEditPost",
Boolean.valueOf(currentUserIsAuthor || isAdmin());
And then in your view just reference $canEditPost.
It's generally better for the view to just reference a simple flag in the model than have the view/template doing the actual logic.
Does your Author object implement equals in such a way that each author is unique?
If so, you could simply check if the Author is the same as the current user (You'd have two sets of tags).
you can have conditions
<% if(mycondition.isTrue()){ %>
<security:authorize ifAnyGranted="ROLE_ADMIN">
Edit: Edit
</security:author
<% }%>
#matt b's answer is a great way to do it and is probably what I'll end up doing. But I found another way that is a bit more complicated but will achieve what I put in this post.
I did a bit of reading and found out that you can handle security at the domain object level and essentially give read/write/delete privileges to a role or to an arbitrary object, for example, the current user ID. In this example, I would give the current user id access to a domain object, in this case a ForumPost object that has its own unique id from the database.
The current user id would then be granted read and write access, which can (via the XML configuration) be defined as a custom role of sorts (I believe the correct term is actually Voter). I could then name this voter MESSAGE__EDIT.
So, in my JSP I could then use the following:
security:authorize ifAnyGranted="MESSAGE_EDIT"
And it would (again, through XML configuration) get the current user id and give access based on the current domain object, in this case, a ForumPost object.
It is a fair bit more work than it sounds, but it can definitely be done.
Some documentation on all this can be found in the Domain Object Security section in the Spring Security Reference Documentation (http://static.springframework.org/spring-security/site/reference/html/springsecurity.html (for some reason the link to the Domain object Security section is broken for now)).
Related
I'm trying to set up my website to allow location additions to the urls.
EG: mysite.com/us/ca/sanfrancisco/home
While also still allowing mysite.com/home and everything in between.
Spring boot parent so you know what version of spring I'm using:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
If there's another piece of versioning you need, let me know.
I get that I can add in regex variables to the request mapping, but how would I go about persisting those urls across more requests?
So right now for an example, the testing error page I have:
#RequestMapping({"/error/501", "/{state:[a-z]{2}}/error/501", "/{state:[a-z]{2}}/{city:[a-z]+}/error/501"})
public ModelAndView testingLocations(ModelMap model, #PathVariable(value="state", required = false) String state,
#PathVariable(value="city", required=false) String city){
getLogger().info("State: {}", state);
model.addAttribute("stateTest",state+":"+city);
model.addAttribute("view", "error");
return new ModelAndView("error/501", model);
}
But when I'm on my testing page, and I click the home button, it takes me back to mysite.com/home
So My Questions
Is there a way for me to persist it so that if they're currently on a location based url, it will apply that base to the future navigations? (unless they manually enter the url to not include them)
Then as a follow-up, is there a way for me to globally apply these request variables without requiring me to add the #PathVariable to every request mapping method? I get that I can just add the request mapping variable strings themselves to the controller class, so that I don't need those on every method. But is there a way for me to utilize those without needing the #PathVariable annotations?
Finally, is there a way for me to make this not as hardcoded, like a way for me to say /{*location}/error to cover as deep as the locations will allow? While still having the verification on the location formatting, so verifying that 1 we support the locations given, 2 the format is correct (/ca/sanfrancisco vs /anything/anything
The last one I can live with, if I need to have the /state/city/municipality/actualtarget
As far as verifying that we support the locations given, I understand that's on my end, which I'll probably just have a small database to keep track of where we do and do not support for the given variables.
Is there a best practice for building this system? I tried to find something on this, but googling "spring boot location url" is not the best at giving me what I need, since "location" can apply to a pretty wide range of topics. I've gotten to where I am from searching, but I can't seem to pin down these last few steps.
Any help/advice/suggestions is appreciated. If upgrading versions is required, I'm not sure how viable that is at the moment, I'd have to look into it. Preferably I'd like the solution to be able to be done on the current spring version I'm running.
The best way here is:
#RequestMapping("/some/{foo}/{baz}")
public String hi(CompositeObject compositeObject) {
return "hi";
}
#Data
public class CompositeObject {
private String foo;
private String baz;
}
Spring provides functionality for request path and request parameters to collect it into a composite object. It doesn' work either with body or headers.
If you have something optional like state, then just keep it null at the controller and handle later
i got a simple programm that begins on an input form where the user fills in 3 numbers. The form action refers to a controller servlet where i store the data in the Bean class with the setter methods I have defined.
number.setNumber1(Double.parseDouble(request.getParameter("number1")));
Till now I stored the Number object in the request with
request.setAttribute("numbers", number);
and forwarded it to the output page where i could get it with ${numbers.biggestNumber ( getter-Method that simply determines the biggest Number) }. A tutorial i am using says I could also get the data directly from the Bean by using this piece of code:
<jsp:useBean id="num" scope="session" class="model.Numbers"/>
<c:out value="${num.biggestNumber}/>
but somehow the Bean uses another object of the Numbers-class. I see the advantage of this technique, because I dont have to put the Numbers object into the request. Can someone tell me how I can use the same Numbers object I stored the data before?
I already read that I shouldnt use "jsp:setProperty..." to store the data on the input page, but if i cant get the information i wrote manually to the Bean, I have to ask myself why I should use the JSP JavaBeans annotation at all.
I used the search function but could not find an answer suitable to my question, or maybe I am just not experienced enough to get them in a more advanced context... Any help would be welcome
if you are using this one,
request.setAttribute("numbers", number);
And using requestDispacher redirecting then at target page you can do likewise,
into JSP file :
<jsp:useBean id="numbers" scope="request" class="model.Numbers"/>
<c:out value="${numbers.biggestNumber}/>
Here,
you did with wrong scope=session, means you are putting value into 'request' scope and try to pull it from session is wrong.
also maintain name of attribute 'same' while putting/getting from scope. here, name="numbers" maintain while putting/getting
Okay my mistake was that I thought JavaBeans-Jsp-Tags would save time and code. Indeed I had to create a HttpSession-Object that stores the ID of the used JavaBean
HttpSession sess = request.getSession(true);
sess.setAttribute("number", number);
Now the JavaBean-Tag in my Output.jsp knows which object to use (the one created in the Input.jsp). As far as I do understand now, the only advantage of the JavaBean-Jsp-Tag above normal Parameters added to the Request is that I can use the Bean-Class in the whole Session and am not dependend on the Request-Object.
I'm currently developing a Struts 2 web application that allow anonymous usage. I want that with anonymous user, the URL will be like:
http://localhost:8080/myapp
But after user logged in, the URL will be personalized base on user name, for example:
http://localhost:8080/myapp/Cuong-Doan
Please suggest me a plugin/technique that can help me to achieve it.
You can try to do it with Tuckey's UrlRewriteFilter. After loging add session attribute, for example loggedUsername=Cuong-Doan. Then analyze this attribute in UrlRewriteFilter rule using session-attribute parameter of condition element. If it is present -> do redirect, add it to the URL using backreferences.
Personalized could be done via setting the parameter specifying the persona. Looking at the URLs in the question I decided to give you imagination about technique used to reproduce SEO optimized URLs. This technique is called "Parameters after action names". For example to map your persona with the action you can use
#Action(value = "/*", params = {"person", "{1}"})
so, after that action is mapped you will get the information about person via the person parameter that could be used in the action.
I have a wicket web application with Page mounted to bookmarkable alias. The page contains a form object with submit action.
The problem is that though the form belongs to the page the action url doesn't contain page alias, but rather created in cryptic form of wicket action.
Is there a way to adjust that behavior, so link will be like page_alias/submit ?
...
setRenderStrategy(IRequestCycleSettings.ONE_PASS_RENDER);
mountBookmarkablePage("/resetpwd", ResetPasswordPage.class);
...
public ResetPasswordPage(final String id, final PageParameters parameters) {
final Form form = new StatelessForm();
form.add(new Button("submit") {
public void onSubmit() {
...
});
add(form);
If you subclass StatelessForm instead of Form, this will take you part of the way. Rather than having something like
action="myapp/?wicket:interface=:1:eventEditor::IFormSubmitListener::"
with the page containing the form mounted at a bookmarkable URL, you'll get something like, for example,
action="myapp/mount/path/some/params/?wicket:interface=:0:eventEditor::IFormSubmitListener::"
This uses a MixedParamUrlCodingStrategy for the mount in WebApplication.init()
You can then override encodeUrlInHiddenFields() to return true, which will give you a clean URL in the action attribute.
However, all this doesn't really change the way Wicket works with forms, i. e., you still have some Wicket-specific state data in the client's markup. The reason why this is so hard, I believe, is that Wicket is meant to help you build a web app that has state. I noticed that Wicket does a lot of stuff (like comparing submitted form values with what the model's getters return before the setters are called) behind the scenes, that I know too little about to be comfortable when just cutting it out.
You can use Wicket to provide RESTful web services, though, as outlined in this blog post. There's also a project on Google code called wicket-rest that expands on this idea. Note that this seems to work as simple as it does because it just never uses the whole component based UI building stuff.
The guy who wrote this post had a different problem, but it helped me understand Wicket forms a little better anyway.
you can hide a lot of the request mumbo jumbo by using a HybridUrlCodingStrategy like so:
mount(new HybridUrlCodingStrategy("/resetpwd", ResetPasswordPage.class));
Then when the click submit, assuming you don't redirect to a new page, the url would change from
mysite.com/DocRoot/resetpwd
to
mysite.com/DocRoot/resetpwd.1
or if you really want it to be mysite.com/DocRoot/resetpwd/submit you could create a new bookmarkable page, say ResetPasswordResult.class, set your response page to that and mount it at "/resetpwd/submit"
You might look at other encoding strategies to see if their is another that suits you better:
http://cwiki.apache.org/WICKET/url-coding-strategies.html
You can take a look at http://day-to-day-stuff.blogspot.com/2008/10/wicket-extreme-consistent-urls.html and try to adapt that for Forms.
I'm using Spring, but this question applies to all JSP-controller type designs.
The JSP page references data (using tags) which is populated by the corresponding controller. My question is, where is the appropriate place to perform formatting, in JSP or the controller?
So far I've been preparing the data by formatting it in my controller.
public class ViewPersonController extends org.springframework.web.servlet.mvc.AbstractController
{
private static final Format MY_DATE_FORMAT = new SimpleDateFormat(...);
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
{
Person person = get person from backing service layer or database
Map properties = new HashMap();
// No formatting required, name is a String
properties.put("name", person.getName());
// getBirthDate() returns Date and is formatted by a Format
properties.put("birthDate", MY_DATE_FORMAT.format(person.getBirthDate()));
// latitude and longitude are separate fields in Person, but in the UI it's one field
properties.put("location", person.getLatitude() + ", " + person.getLongitude());
return new ModelAndView("viewPerson", "person", properties);
}
}
The JSP file would look something like:
Name = <c:out value="${person. name}" /><br>
Birth Date = <c:out value="${person. birthDate}" /><br>
Location = <c:out value="${person. location}" /><br>
I know that JSP does have some provisions for formatting,
<%# taglib uri="http://java.sun.com/jstl/fmt" prefix="fmt" %>
<fmt:formatDate type="date" value="${person. birthDate}" />
But this only works with Java's java.util.Format. What if I need more complex or computed values. In such a case putting the code in the JSP would be cumbersome (and ugly).
I'm curious if this is following the spirit Spring/JSP/MVC. In other words, is the controller part of the view? Where is the preferred place to perform view related formatting? Should my controller just be returning the object (Person) instead of a Map of formatted values?
JSPs typically do not have a lot (or any?) code in them, so your options would be
controller
tag libraries
I would say that a tag library would probably be what you want for most cases, because typically the view is the code that cares about things like formatting.
If standard tag libraries don't get you there, they are not hard to create, so you can roll your own.
I typically do formatting, etc. in the bean or a view "helper". This has several advantages including the following:
Easier to test
Flexibility to change your view technologies without worrying about porting or rewriting what you've done in custom tablibs.
Cleaner and easier to maintain controller and view code.
I prefer to consider formatting part of the display layer, thus done in the JSP. I used Velocity most recently, but same idea with JSP: controller returns a data model, and the view is responsible for rendering that data into a visible representation. Plenty of JSP tag libraries out there for common needs.
You mention complex or computed values. Those sound like elements of the results data model to me, so should be done in the controller, even if they can in principle be determined by other data, such as sum, max and other aggregate values. By formatting in the view I mean basic things like date and number formats, line splitting, alignment. Of course the exact line between data and formatted representation depends on the application, but I think you get the idea.
The way I would do it is -
an instance of the Person class would be the only object in the Model of the ModelAndView
I would move the "presentation logic" into the Person class itself. For example,
public class Person {
public String getLocation() {
return this.latitude.concat(", ").concat(this.longitude);
}
}
I think overall this approach:
1 - strengthens your domain model.
2 - reduces code duplication (what if you wanted to show the location in another JSP? With your approach you'd have a lot of code duplicated)