Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Created Freemarker tags for Apache Shiro, these mirror the JSP tags

provided by Shiro itself. See README.md for usage.
  • Loading branch information...
commit b876e1a2b72d9294a1cacedb5b22316eb0441881 0 parents
@jagregory authored
3  .gitignore
@@ -0,0 +1,3 @@
+target
+.idea
+*.iml
23 README.md
@@ -0,0 +1,23 @@
+# Apache Shiro tags for Freemarker
+
+[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.
+
+## Install
+
+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.
+
+If there's enough demand, I could put this up on Maven.
+
+## Usage
+
+Declare a shared variable called "shiro", and assign it to an instance of the ShiroTags class.
+
+ cfg.setSharedVariable("shiro", new ShiroTags());
+
+You should then be able to use the tags in your Freemarker templates.
+
+ <@shiro.guest>Hello guest!</@shiro.guest>
+
+## License
+
+Do what you want with it, just don't blame me if it breaks anything.
BIN  dist/shiro-freemarker-tags-0.1-SNAPSHOT.jar
Binary file not shown
32 pom.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>shiro-freemarker-tags</artifactId>
+ <groupId>com.jagregory</groupId>
+ <version>0.1-SNAPSHOT</version>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.shiro</groupId>
+ <artifactId>shiro-all</artifactId>
+ <version>1.1.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.freemarker</groupId>
+ <artifactId>freemarker</artifactId>
+ <version>2.3.18</version>
+ </dependency>
+ </dependencies>
+
+ <repositories>
+ <repository>
+ <id>central</id>
+ <name>Maven2 Repository</name>
+ <url>http://repo1.maven.org/maven2/</url>
+ </repository>
+ </repositories>
+</project>
44 src/main/java/com/jagregory/shiro/freemarker/AuthenticatedTag.java
@@ -0,0 +1,44 @@
+package com.jagregory.shiro.freemarker;
+
+import freemarker.core.Environment;
+import freemarker.log.Logger;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateException;
+
+import java.io.IOException;
+import java.util.Map;
+
+
+/**
+ * JSP tag that renders the tag body only if the current user has executed a <b>successful</b> authentication attempt
+ * <em>during their current session</em>.
+ *
+ * <p>This is more restrictive than the {@link UserTag}, which only
+ * ensures the current user is known to the system, either via a current login or from Remember Me services,
+ * which only makes the assumption that the current user is who they say they are, and does not guarantee it like
+ * this tag does.
+ *
+ * <p>The logically opposite tag of this one is the {@link NotAuthenticatedTag}
+ *
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.AuthenticatedTag}</p>
+ *
+ * @since 0.2
+ */
+public class AuthenticatedTag extends SecureTag {
+ private static final Logger log = Logger.getLogger("AuthenticatedTag");
+
+ @Override
+ public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException {
+ if (getSubject() != null && getSubject().isAuthenticated()) {
+ if (log.isDebugEnabled()) {
+ log.debug("Subject exists and is authenticated. Tag body will be evaluated.");
+ }
+
+ renderBody(env, body);
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Subject does not exist or is not authenticated. Tag body will not be evaluated.");
+ }
+ }
+ }
+}
42 src/main/java/com/jagregory/shiro/freemarker/GuestTag.java
@@ -0,0 +1,42 @@
+package com.jagregory.shiro.freemarker;
+
+import freemarker.core.Environment;
+import freemarker.log.Logger;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateException;
+
+import java.io.IOException;
+import java.util.Map;
+
+
+/**
+ * JSP tag that renders the tag body if the current user <em>is not</em> known to the system, either because they
+ * haven't logged in yet, or because they have no 'RememberMe' identity.
+ *
+ * <p>The logically opposite tag of this one is the {@link UserTag}. Please read that class's JavaDoc as it explains
+ * more about the differences between Authenticated/Unauthenticated and User/Guest semantic differences.
+ *
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.GuestTag}</p>
+ *
+ * @since 0.9
+ */
+public class GuestTag extends SecureTag {
+ private static final Logger log = Logger.getLogger("AuthenticatedTag");
+
+ @Override
+ public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException {
+ if (getSubject() == null || getSubject().getPrincipal() == null) {
+ if (log.isDebugEnabled()) {
+ log.debug("Subject does not exist or does not have a known identity (aka 'principal'). " +
+ "Tag body will be evaluated.");
+ }
+
+ renderBody(env, body);
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Subject exists or has a known identity (aka 'principal'). " +
+ "Tag body will not be evaluated.");
+ }
+ }
+ }
+}
51 src/main/java/com/jagregory/shiro/freemarker/HasAnyRolesTag.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package com.jagregory.shiro.freemarker;
+
+import org.apache.shiro.subject.Subject;
+
+
+/**
+ * Displays body content if the current user has any of the roles specified.
+ *
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.HasAnyRolesTag}</p>
+ *
+ * @since 0.2
+ */
+public class HasAnyRolesTag extends RoleTag {
+ // Delimeter that separates role names in tag attribute
+ private static final String ROLE_NAMES_DELIMETER = ",";
+
+ protected boolean showTagBody(String roleNames) {
+ boolean hasAnyRole = false;
+ Subject subject = getSubject();
+
+ if (subject != null) {
+ // Iterate through roles and check to see if the user has one of the roles
+ for (String role : roleNames.split(ROLE_NAMES_DELIMETER)) {
+ if (subject.hasRole(role.trim())) {
+ hasAnyRole = true;
+ break;
+ }
+ }
+ }
+
+ return hasAnyRole;
+ }
+}
12 src/main/java/com/jagregory/shiro/freemarker/HasPermissionTag.java
@@ -0,0 +1,12 @@
+package com.jagregory.shiro.freemarker;
+
+/**
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.HasPermissionTag}</p>
+ *
+ * @since 0.1
+ */
+public class HasPermissionTag extends PermissionTag {
+ protected boolean showTagBody(String p) {
+ return isPermitted(p);
+ }
+}
10 src/main/java/com/jagregory/shiro/freemarker/HasRoleTag.java
@@ -0,0 +1,10 @@
+package com.jagregory.shiro.freemarker;
+
+/**
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.HasRoleTag}</p>
+ */
+public class HasRoleTag extends RoleTag {
+ protected boolean showTagBody(String roleName) {
+ return getSubject() != null && getSubject().hasRole(roleName);
+ }
+}
10 src/main/java/com/jagregory/shiro/freemarker/LacksPermissionTag.java
@@ -0,0 +1,10 @@
+package com.jagregory.shiro.freemarker;
+
+/**
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.LacksPermissionTag}</p>
+ */
+public class LacksPermissionTag extends PermissionTag {
+ protected boolean showTagBody(String p) {
+ return !isPermitted(p);
+ }
+}
11 src/main/java/com/jagregory/shiro/freemarker/LacksRoleTag.java
@@ -0,0 +1,11 @@
+package com.jagregory.shiro.freemarker;
+
+/**
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.LacksRoleTag}</p>
+ */
+public class LacksRoleTag extends RoleTag {
+ protected boolean showTagBody(String roleName) {
+ boolean hasRole = getSubject() != null && getSubject().hasRole(roleName);
+ return !hasRole;
+ }
+}
32 src/main/java/com/jagregory/shiro/freemarker/NotAuthenticatedTag.java
@@ -0,0 +1,32 @@
+package com.jagregory.shiro.freemarker;
+
+import freemarker.core.Environment;
+import freemarker.log.Logger;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateException;
+
+import java.io.IOException;
+import java.util.Map;
+
+
+/**
+ * Freemarker tag that renders the tag body only if the current user has <em>not</em> executed a successful authentication
+ * attempt <em>during their current session</em>.
+ *
+ * <p>The logically opposite tag of this one is the {@link org.apache.shiro.web.tags.AuthenticatedTag}.
+ *
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.NotAuthenticatedTag}</p>
+ */
+public class NotAuthenticatedTag extends SecureTag {
+ static final Logger log = Logger.getLogger("NotAuthenticatedTag");
+
+ @Override
+ public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException {
+ if (getSubject() == null || !getSubject().isAuthenticated()) {
+ log.debug("Subject does not exist or is not authenticated. Tag body will be evaluated.");
+ renderBody(env, body);
+ } else {
+ log.debug("Subject exists and is authenticated. Tag body will not be evaluated.");
+ }
+ }
+}
42 src/main/java/com/jagregory/shiro/freemarker/PermissionTag.java
@@ -0,0 +1,42 @@
+package com.jagregory.shiro.freemarker;
+
+import freemarker.core.Environment;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModelException;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.PermissionTag}</p>
+ */
+public abstract class PermissionTag extends SecureTag {
+ String getName(Map params) {
+ return getParam(params, "name");
+ }
+
+ @Override
+ protected void verifyParameters(Map params) throws TemplateModelException {
+ String permission = getName(params);
+
+ if (permission == null || permission.length() == 0) {
+ throw new TemplateModelException("The 'name' tag attribute must be set.");
+ }
+ }
+
+ @Override
+ public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException {
+ String p = getName(params);
+
+ boolean show = showTagBody(p);
+ if (show) {
+ renderBody(env, body);
+ }
+ }
+
+ protected boolean isPermitted(String p) {
+ return getSubject() != null && getSubject().isPermitted(p);
+ }
+
+ protected abstract boolean showTagBody(String p);
+}
119 src/main/java/com/jagregory/shiro/freemarker/PrincipalTag.java
@@ -0,0 +1,119 @@
+package com.jagregory.shiro.freemarker;
+
+import freemarker.core.Environment;
+import freemarker.log.Logger;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateModelException;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <p>Tag used to print out the String value of a user's default principal,
+ * or a specific principal as specified by the tag's attributes.</p>
+ *
+ * <p> If no attributes are specified, the tag prints out the <tt>toString()</tt>
+ * value of the user's default principal. If the <tt>type</tt> attribute
+ * is specified, the tag looks for a principal with the given type. If the
+ * <tt>property</tt> attribute is specified, the tag prints the string value of
+ * the specified property of the principal. If no principal is found or the user
+ * is not authenticated, the tag displays nothing unless a <tt>defaultValue</tt>
+ * is specified.</p>
+ *
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.PrincipalTag}</p>
+ *
+ * @since 0.2
+ */
+public class PrincipalTag extends SecureTag {
+ static final Logger log = Logger.getLogger("PrincipalTag");
+
+ /**
+ * The type of principal to be retrieved, or null if the default principal should be used.
+ */
+ String getType(Map params) {
+ return getParam(params, "type");
+ }
+
+ /**
+ * The property name to retrieve of the principal, or null if the <tt>toString()</tt> value should be used.
+ */
+ String getProperty(Map params) {
+ return getParam(params, "property");
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException {
+ String result = null;
+
+ if (getSubject() != null) {
+ // Get the principal to print out
+ Object principal;
+
+ if (getType(params) == null) {
+ principal = getSubject().getPrincipal();
+ } else {
+ principal = getPrincipalFromClassName(params);
+ }
+
+ // Get the string value of the principal
+ if (principal != null) {
+ String property = getProperty(params);
+
+ if (property == null) {
+ result = principal.toString();
+ } else {
+ result = getPrincipalProperty(principal, property);
+ }
+ }
+ }
+
+ // Print out the principal value if not null
+ if (result != null) {
+ try {
+ env.getOut().write(result);
+ } catch (IOException ex) {
+ throw new TemplateException("Error writing ["+result+"] to Freemarker.", ex, env);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ Object getPrincipalFromClassName(Map params) {
+ String type = getType(params);
+
+ try {
+ Class cls = Class.forName(type);
+
+ return getSubject().getPrincipals().oneByType(cls);
+ } catch (ClassNotFoundException ex) {
+ log.error("Unable to find class for name ["+type+"]", ex);
+ }
+
+ return null;
+ }
+
+ String getPrincipalProperty(Object principal, String property) throws TemplateModelException {
+ try {
+ BeanInfo beanInfo = Introspector.getBeanInfo(principal.getClass());
+
+ // Loop through the properties to get the string value of the specified property
+ for (PropertyDescriptor propertyDescriptor : beanInfo.getPropertyDescriptors()) {
+ if (propertyDescriptor.getName().equals(property)) {
+ Object value = propertyDescriptor.getReadMethod().invoke(principal, (Object[]) null);
+
+ return String.valueOf(value);
+ }
+ }
+
+ // property not found, throw
+ throw new TemplateModelException("Property ["+property+"] not found in principal of type ["+principal.getClass().getName()+"]");
+ } catch (Exception ex) {
+ throw new TemplateModelException("Error reading property ["+property+"] from principal of type ["+principal.getClass().getName()+"]", ex);
+ }
+ }
+}
26 src/main/java/com/jagregory/shiro/freemarker/RoleTag.java
@@ -0,0 +1,26 @@
+package com.jagregory.shiro.freemarker;
+
+import freemarker.core.Environment;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateException;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.RoleTag}</p>
+ */
+public abstract class RoleTag extends SecureTag {
+ String getName(Map params) {
+ return getParam(params, "name");
+ }
+
+ @Override
+ public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException {
+ boolean show = showTagBody(getName(params));
+ if (show) {
+ renderBody(env, body);
+ }
+ }
+
+ protected abstract boolean showTagBody(String roleName);
+}
43 src/main/java/com/jagregory/shiro/freemarker/SecureTag.java
@@ -0,0 +1,43 @@
+package com.jagregory.shiro.freemarker;
+
+import freemarker.core.Environment;
+import freemarker.template.*;
+import org.apache.shiro.SecurityUtils;
+import org.apache.shiro.subject.Subject;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.SecureTag}</p>
+ */
+public abstract class SecureTag implements TemplateDirectiveModel {
+ public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException {
+ verifyParameters(params);
+ render(env, params, body);
+ }
+
+ public abstract void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException;
+
+ protected String getParam(Map params, String name) {
+ Object value = params.get(name);
+
+ if (value instanceof SimpleScalar) {
+ return ((SimpleScalar)value).getAsString();
+ }
+
+ return null;
+ }
+
+ protected Subject getSubject() {
+ return SecurityUtils.getSubject();
+ }
+
+ protected void verifyParameters(Map params) throws TemplateModelException {
+ }
+
+ protected void renderBody(Environment env, TemplateDirectiveBody body) throws IOException, TemplateException {
+ if (body != null) {
+ body.render(env.getOut());
+ }
+ }
+}
23 src/main/java/com/jagregory/shiro/freemarker/ShiroTags.java
@@ -0,0 +1,23 @@
+package com.jagregory.shiro.freemarker;
+
+import freemarker.template.SimpleHash;
+
+/**
+ * Shortcut for injecting the tags into Freemarker
+ *
+ * <p>Usage: cfg.setSharedVeriable("shiro", new ShiroTags());</p>
+ */
+public class ShiroTags extends SimpleHash {
+ public ShiroTags() {
+ put("authenticated", new AuthenticatedTag());
+ put("guest", new GuestTag());
+ put("hasAnyRoles", new HasAnyRolesTag());
+ put("hasPermission", new HasPermissionTag());
+ put("hasRole", new HasRoleTag());
+ put("lacksPermission", new LacksPermissionTag());
+ put("lacksRole", new LacksRoleTag());
+ put("notAuthenticated", new NotAuthenticatedTag());
+ put("principal", new PrincipalTag());
+ put("user", new UserTag());
+ }
+}
37 src/main/java/com/jagregory/shiro/freemarker/UserTag.java
@@ -0,0 +1,37 @@
+package com.jagregory.shiro.freemarker;
+
+import freemarker.core.Environment;
+import freemarker.log.Logger;
+import freemarker.template.TemplateDirectiveBody;
+import freemarker.template.TemplateException;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Freemarker tag that renders the tag body if the current user known to the system, either from a successful login attempt
+ * (not necessarily during the current session) or from 'RememberMe' services.
+ *
+ * <p><b>Note:</b> This is <em>less</em> restrictive than the <code>AuthenticatedTag</code> since it only assumes
+ * the user is who they say they are, either via a current session login <em>or</em> via Remember Me services, which
+ * makes no guarantee the user is who they say they are. The <code>AuthenticatedTag</code> however
+ * guarantees that the current user has logged in <em>during their current session</em>, proving they really are
+ * who they say they are.
+ *
+ * <p>The logically opposite tag of this one is the {@link org.apache.shiro.web.tags.GuestTag}.
+ *
+ * <p>Equivalent to {@link org.apache.shiro.web.tags.UserTag}</p>
+ */
+public class UserTag extends SecureTag {
+ static final Logger log = Logger.getLogger("UserTag");
+
+ @Override
+ public void render(Environment env, Map params, TemplateDirectiveBody body) throws IOException, TemplateException {
+ if (getSubject() != null && getSubject().getPrincipal() != null) {
+ log.debug("Subject has known identity (aka 'principal'). Tag body will be evaluated.");
+ renderBody(env, body);
+ } else {
+ log.debug("Subject does not exist or have a known identity (aka 'principal'). Tag body will not be evaluated.");
+ }
+ }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.