├── .gitignore ├── dist └── shiro-freemarker-tags-0.1-SNAPSHOT.jar ├── src └── main │ └── java │ └── com │ └── jagregory │ └── shiro │ └── freemarker │ ├── LacksPermissionTag.java │ ├── HasRoleTag.java │ ├── HasPermissionTag.java │ ├── LacksRoleTag.java │ ├── RoleTag.java │ ├── ShiroTags.java │ ├── NotAuthenticatedTag.java │ ├── PermissionTag.java │ ├── SecureTag.java │ ├── GuestTag.java │ ├── AuthenticatedTag.java │ ├── UserTag.java │ ├── HasAnyRolesTag.java │ └── PrincipalTag.java ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .idea 3 | *.iml -------------------------------------------------------------------------------- /dist/shiro-freemarker-tags-0.1-SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jagregory/shiro-freemarker-tags/HEAD/dist/shiro-freemarker-tags-0.1-SNAPSHOT.jar -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/LacksPermissionTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | /** 4 | *
Equivalent to {@link org.apache.shiro.web.tags.LacksPermissionTag}
5 | */ 6 | public class LacksPermissionTag extends PermissionTag { 7 | protected boolean showTagBody(String p) { 8 | return !isPermitted(p); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/HasRoleTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | /** 4 | *Equivalent to {@link org.apache.shiro.web.tags.HasRoleTag}
5 | */ 6 | public class HasRoleTag extends RoleTag { 7 | protected boolean showTagBody(String roleName) { 8 | return getSubject() != null && getSubject().hasRole(roleName); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/HasPermissionTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | /** 4 | *Equivalent to {@link org.apache.shiro.web.tags.HasPermissionTag}
5 | * 6 | * @since 0.1 7 | */ 8 | public class HasPermissionTag extends PermissionTag { 9 | protected boolean showTagBody(String p) { 10 | return isPermitted(p); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/LacksRoleTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | /** 4 | *Equivalent to {@link org.apache.shiro.web.tags.LacksRoleTag}
5 | */ 6 | public class LacksRoleTag extends RoleTag { 7 | protected boolean showTagBody(String roleName) { 8 | boolean hasRole = getSubject() != null && getSubject().hasRole(roleName); 9 | return !hasRole; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/RoleTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | import freemarker.core.Environment; 4 | import freemarker.template.TemplateDirectiveBody; 5 | import freemarker.template.TemplateException; 6 | import java.io.IOException; 7 | import java.util.Map; 8 | 9 | /** 10 | *Equivalent to {@link org.apache.shiro.web.tags.RoleTag}
11 | */ 12 | public abstract class RoleTag extends SecureTag { 13 | String getName(Map params) { 14 | return getParam(params, "name"); 15 | } 16 | 17 | @Override 18 | public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException { 19 | boolean show = showTagBody(getName(params)); 20 | if (show) { 21 | renderBody(env, body); 22 | } 23 | } 24 | 25 | protected abstract boolean showTagBody(String roleName); 26 | } -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/ShiroTags.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | import freemarker.template.SimpleHash; 4 | 5 | /** 6 | * Shortcut for injecting the tags into Freemarker 7 | * 8 | *Usage: cfg.setSharedVeriable("shiro", new ShiroTags());
9 | */ 10 | public class ShiroTags extends SimpleHash { 11 | public ShiroTags() { 12 | put("authenticated", new AuthenticatedTag()); 13 | put("guest", new GuestTag()); 14 | put("hasAnyRoles", new HasAnyRolesTag()); 15 | put("hasPermission", new HasPermissionTag()); 16 | put("hasRole", new HasRoleTag()); 17 | put("lacksPermission", new LacksPermissionTag()); 18 | put("lacksRole", new LacksRoleTag()); 19 | put("notAuthenticated", new NotAuthenticatedTag()); 20 | put("principal", new PrincipalTag()); 21 | put("user", new UserTag()); 22 | } 23 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apache Shiro tags for Freemarker 2 | 3 | [Apache Shiro](http://shiro.apache.org/) comes with some [handy JSP tags](http://shiro.apache.org/jsp-tag-library.html) for doing things like only showing content for anonymous users, logged in users, etc... I'm using [Freemarker](http://freemarker.sourceforge.net/) and didn't want to take a dependency on JSP just for Shiro, so I rewrote the tags for Freemarker. 4 | 5 | ## Install 6 | 7 | Either download the [dist/shiro-freemarker-tags-0.1-SNAPSHOT.jar](/jagregory/shiro-freemarker-tags/raw/master/dist/shiro-freemarker-tags-0.1-SNAPSHOT.jar) or take all the java files and stick them in your project. Simple. 8 | 9 | If there's enough demand, I could put this up on Maven. 10 | 11 | ## Usage 12 | 13 | Declare a shared variable called "shiro", and assign it to an instance of the ShiroTags class. 14 | 15 | cfg.setSharedVariable("shiro", new ShiroTags()); 16 | 17 | You should then be able to use the tags in your Freemarker templates. 18 | 19 | <@shiro.guest>Hello guest!@shiro.guest> 20 | 21 | ## License 22 | 23 | Do what you want with it, just don't blame me if it breaks anything. -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/NotAuthenticatedTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | import freemarker.core.Environment; 4 | import freemarker.log.Logger; 5 | import freemarker.template.TemplateDirectiveBody; 6 | import freemarker.template.TemplateException; 7 | 8 | import java.io.IOException; 9 | import java.util.Map; 10 | 11 | 12 | /** 13 | * Freemarker tag that renders the tag body only if the current user has not executed a successful authentication 14 | * attempt during their current session. 15 | * 16 | *The logically opposite tag of this one is the {@link org.apache.shiro.web.tags.AuthenticatedTag}. 17 | * 18 | *
Equivalent to {@link org.apache.shiro.web.tags.NotAuthenticatedTag}
19 | */ 20 | public class NotAuthenticatedTag extends SecureTag { 21 | static final Logger log = Logger.getLogger("NotAuthenticatedTag"); 22 | 23 | @Override 24 | public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException { 25 | if (getSubject() == null || !getSubject().isAuthenticated()) { 26 | log.debug("Subject does not exist or is not authenticated. Tag body will be evaluated."); 27 | renderBody(env, body); 28 | } else { 29 | log.debug("Subject exists and is authenticated. Tag body will not be evaluated."); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/PermissionTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | import freemarker.core.Environment; 4 | import freemarker.template.TemplateDirectiveBody; 5 | import freemarker.template.TemplateException; 6 | import freemarker.template.TemplateModelException; 7 | import java.io.IOException; 8 | import java.util.Map; 9 | 10 | /** 11 | *Equivalent to {@link org.apache.shiro.web.tags.PermissionTag}
12 | */ 13 | public abstract class PermissionTag extends SecureTag { 14 | String getName(Map params) { 15 | return getParam(params, "name"); 16 | } 17 | 18 | @Override 19 | protected void verifyParameters(Map params) throws TemplateModelException { 20 | String permission = getName(params); 21 | 22 | if (permission == null || permission.length() == 0) { 23 | throw new TemplateModelException("The 'name' tag attribute must be set."); 24 | } 25 | } 26 | 27 | @Override 28 | public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException { 29 | String p = getName(params); 30 | 31 | boolean show = showTagBody(p); 32 | if (show) { 33 | renderBody(env, body); 34 | } 35 | } 36 | 37 | protected boolean isPermitted(String p) { 38 | return getSubject() != null && getSubject().isPermitted(p); 39 | } 40 | 41 | protected abstract boolean showTagBody(String p); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/SecureTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | import freemarker.core.Environment; 4 | import freemarker.template.*; 5 | import org.apache.shiro.SecurityUtils; 6 | import org.apache.shiro.subject.Subject; 7 | import java.io.IOException; 8 | import java.util.Map; 9 | 10 | /** 11 | *Equivalent to {@link org.apache.shiro.web.tags.SecureTag}
12 | */ 13 | public abstract class SecureTag implements TemplateDirectiveModel { 14 | public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException { 15 | verifyParameters(params); 16 | render(env, params, body); 17 | } 18 | 19 | public abstract void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException; 20 | 21 | protected String getParam(Map params, String name) { 22 | Object value = params.get(name); 23 | 24 | if (value instanceof SimpleScalar) { 25 | return ((SimpleScalar)value).getAsString(); 26 | } 27 | 28 | return null; 29 | } 30 | 31 | protected Subject getSubject() { 32 | return SecurityUtils.getSubject(); 33 | } 34 | 35 | protected void verifyParameters(Map params) throws TemplateModelException { 36 | } 37 | 38 | protected void renderBody(Environment env, TemplateDirectiveBody body) throws IOException, TemplateException { 39 | if (body != null) { 40 | body.render(env.getOut()); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/GuestTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | import freemarker.core.Environment; 4 | import freemarker.log.Logger; 5 | import freemarker.template.TemplateDirectiveBody; 6 | import freemarker.template.TemplateException; 7 | 8 | import java.io.IOException; 9 | import java.util.Map; 10 | 11 | 12 | /** 13 | * JSP tag that renders the tag body if the current user is not known to the system, either because they 14 | * haven't logged in yet, or because they have no 'RememberMe' identity. 15 | * 16 | *The logically opposite tag of this one is the {@link UserTag}. Please read that class's JavaDoc as it explains 17 | * more about the differences between Authenticated/Unauthenticated and User/Guest semantic differences. 18 | * 19 | *
Equivalent to {@link org.apache.shiro.web.tags.GuestTag}
20 | * 21 | * @since 0.9 22 | */ 23 | public class GuestTag extends SecureTag { 24 | private static final Logger log = Logger.getLogger("AuthenticatedTag"); 25 | 26 | @Override 27 | public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException { 28 | if (getSubject() == null || getSubject().getPrincipal() == null) { 29 | if (log.isDebugEnabled()) { 30 | log.debug("Subject does not exist or does not have a known identity (aka 'principal'). " + 31 | "Tag body will be evaluated."); 32 | } 33 | 34 | renderBody(env, body); 35 | } else { 36 | if (log.isDebugEnabled()) { 37 | log.debug("Subject exists or has a known identity (aka 'principal'). " + 38 | "Tag body will not be evaluated."); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/AuthenticatedTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | import freemarker.core.Environment; 4 | import freemarker.log.Logger; 5 | import freemarker.template.TemplateDirectiveBody; 6 | import freemarker.template.TemplateException; 7 | 8 | import java.io.IOException; 9 | import java.util.Map; 10 | 11 | 12 | /** 13 | * JSP tag that renders the tag body only if the current user has executed a successful authentication attempt 14 | * during their current session. 15 | * 16 | *This is more restrictive than the {@link UserTag}, which only 17 | * ensures the current user is known to the system, either via a current login or from Remember Me services, 18 | * which only makes the assumption that the current user is who they say they are, and does not guarantee it like 19 | * this tag does. 20 | * 21 | *
The logically opposite tag of this one is the {@link NotAuthenticatedTag} 22 | * 23 | *
Equivalent to {@link org.apache.shiro.web.tags.AuthenticatedTag}
24 | * 25 | * @since 0.2 26 | */ 27 | public class AuthenticatedTag extends SecureTag { 28 | private static final Logger log = Logger.getLogger("AuthenticatedTag"); 29 | 30 | @Override 31 | public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException { 32 | if (getSubject() != null && getSubject().isAuthenticated()) { 33 | if (log.isDebugEnabled()) { 34 | log.debug("Subject exists and is authenticated. Tag body will be evaluated."); 35 | } 36 | 37 | renderBody(env, body); 38 | } else { 39 | if (log.isDebugEnabled()) { 40 | log.debug("Subject does not exist or is not authenticated. Tag body will not be evaluated."); 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/UserTag.java: -------------------------------------------------------------------------------- 1 | package com.jagregory.shiro.freemarker; 2 | 3 | import freemarker.core.Environment; 4 | import freemarker.log.Logger; 5 | import freemarker.template.TemplateDirectiveBody; 6 | import freemarker.template.TemplateException; 7 | 8 | import java.io.IOException; 9 | import java.util.Map; 10 | 11 | /** 12 | * Freemarker tag that renders the tag body if the current user known to the system, either from a successful login attempt 13 | * (not necessarily during the current session) or from 'RememberMe' services. 14 | * 15 | *Note: This is less restrictive than the AuthenticatedTag since it only assumes
16 | * the user is who they say they are, either via a current session login or via Remember Me services, which
17 | * makes no guarantee the user is who they say they are. The AuthenticatedTag however
18 | * guarantees that the current user has logged in during their current session, proving they really are
19 | * who they say they are.
20 | *
21 | *
The logically opposite tag of this one is the {@link org.apache.shiro.web.tags.GuestTag}. 22 | * 23 | *
Equivalent to {@link org.apache.shiro.web.tags.UserTag}
24 | */ 25 | public class UserTag extends SecureTag { 26 | static final Logger log = Logger.getLogger("UserTag"); 27 | 28 | @Override 29 | public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException { 30 | if (getSubject() != null && getSubject().getPrincipal() != null) { 31 | log.debug("Subject has known identity (aka 'principal'). Tag body will be evaluated."); 32 | renderBody(env, body); 33 | } else { 34 | log.debug("Subject does not exist or have a known identity (aka 'principal'). Tag body will not be evaluated."); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/jagregory/shiro/freemarker/HasAnyRolesTag.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | package com.jagregory.shiro.freemarker; 20 | 21 | import org.apache.shiro.subject.Subject; 22 | 23 | 24 | /** 25 | * Displays body content if the current user has any of the roles specified. 26 | * 27 | *Equivalent to {@link org.apache.shiro.web.tags.HasAnyRolesTag}
28 | * 29 | * @since 0.2 30 | */ 31 | public class HasAnyRolesTag extends RoleTag { 32 | // Delimeter that separates role names in tag attribute 33 | private static final String ROLE_NAMES_DELIMETER = ","; 34 | 35 | protected boolean showTagBody(String roleNames) { 36 | boolean hasAnyRole = false; 37 | Subject subject = getSubject(); 38 | 39 | if (subject != null) { 40 | // Iterate through roles and check to see if the user has one of the roles 41 | for (String role : roleNames.split(ROLE_NAMES_DELIMETER)) { 42 | if (subject.hasRole(role.trim())) { 43 | hasAnyRole = true; 44 | break; 45 | } 46 | } 47 | } 48 | 49 | return hasAnyRole; 50 | } 51 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 |Tag used to print out the String value of a user's default principal, 17 | * or a specific principal as specified by the tag's attributes.
18 | * 19 | *If no attributes are specified, the tag prints out the toString() 20 | * value of the user's default principal. If the type attribute 21 | * is specified, the tag looks for a principal with the given type. If the 22 | * property attribute is specified, the tag prints the string value of 23 | * the specified property of the principal. If no principal is found or the user 24 | * is not authenticated, the tag displays nothing unless a defaultValue 25 | * is specified.
26 | * 27 | *Equivalent to {@link org.apache.shiro.web.tags.PrincipalTag}
28 | * 29 | * @since 0.2 30 | */ 31 | public class PrincipalTag extends SecureTag { 32 | static final Logger log = Logger.getLogger("PrincipalTag"); 33 | 34 | /** 35 | * The type of principal to be retrieved, or null if the default principal should be used. 36 | */ 37 | String getType(Map params) { 38 | return getParam(params, "type"); 39 | } 40 | 41 | /** 42 | * The property name to retrieve of the principal, or null if the toString() value should be used. 43 | */ 44 | String getProperty(Map params) { 45 | return getParam(params, "property"); 46 | } 47 | 48 | @SuppressWarnings("unchecked") 49 | @Override 50 | public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException { 51 | String result = null; 52 | 53 | if (getSubject() != null) { 54 | // Get the principal to print out 55 | Object principal; 56 | 57 | if (getType(params) == null) { 58 | principal = getSubject().getPrincipal(); 59 | } else { 60 | principal = getPrincipalFromClassName(params); 61 | } 62 | 63 | // Get the string value of the principal 64 | if (principal != null) { 65 | String property = getProperty(params); 66 | 67 | if (property == null) { 68 | result = principal.toString(); 69 | } else { 70 | result = getPrincipalProperty(principal, property); 71 | } 72 | } 73 | } 74 | 75 | // Print out the principal value if not null 76 | if (result != null) { 77 | try { 78 | env.getOut().write(result); 79 | } catch (IOException ex) { 80 | throw new TemplateException("Error writing ["+result+"] to Freemarker.", ex, env); 81 | } 82 | } 83 | } 84 | 85 | @SuppressWarnings("unchecked") 86 | Object getPrincipalFromClassName(Map params) { 87 | String type = getType(params); 88 | 89 | try { 90 | Class cls = Class.forName(type); 91 | 92 | return getSubject().getPrincipals().oneByType(cls); 93 | } catch (ClassNotFoundException ex) { 94 | log.error("Unable to find class for name ["+type+"]", ex); 95 | } 96 | 97 | return null; 98 | } 99 | 100 | String getPrincipalProperty(Object principal, String property) throws TemplateModelException { 101 | try { 102 | BeanInfo beanInfo = Introspector.getBeanInfo(principal.getClass()); 103 | 104 | // Loop through the properties to get the string value of the specified property 105 | for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) { 106 | if (propertyDescriptor.getName().equals(property)) { 107 | Object value = propertyDescriptor.getReadMethod().invoke(principal, (Object[]) null); 108 | 109 | return String.valueOf(value); 110 | } 111 | } 112 | 113 | // property not found, throw 114 | throw new TemplateModelException("Property ["+property+"] not found in principal of type ["+principal.getClass().getName()+"]"); 115 | } catch (Exception ex) { 116 | throw new TemplateModelException("Error reading property ["+property+"] from principal of type ["+principal.getClass().getName()+"]", ex); 117 | } 118 | } 119 | } --------------------------------------------------------------------------------