001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.shiro.realm; 020 021import org.apache.shiro.authc.AuthenticationException; 022import org.apache.shiro.authc.AuthenticationInfo; 023import org.apache.shiro.authc.AuthenticationToken; 024import org.apache.shiro.authc.ExpiredCredentialsException; 025import org.apache.shiro.authc.LockedAccountException; 026import org.apache.shiro.authc.SimpleAccount; 027import org.apache.shiro.authc.UsernamePasswordToken; 028import org.apache.shiro.authz.AuthorizationInfo; 029import org.apache.shiro.authz.SimpleRole; 030import org.apache.shiro.subject.PrincipalCollection; 031import org.apache.shiro.util.CollectionUtils; 032 033import java.util.HashSet; 034import java.util.LinkedHashMap; 035import java.util.Map; 036import java.util.Set; 037import java.util.concurrent.locks.ReadWriteLock; 038import java.util.concurrent.locks.ReentrantReadWriteLock; 039 040/** 041 * A simple implementation of the {@link Realm Realm} interface that 042 * uses a set of configured user accounts and roles to support authentication and authorization. Each account entry 043 * specifies the username, password, and roles for a user. Roles can also be mapped 044 * to permissions and associated with users. 045 * <p/> 046 * User accounts and roles are stored in two {@code Map}s in memory, so it is expected that the total number of either 047 * is not sufficiently large. 048 * 049 * @since 0.1 050 */ 051public class SimpleAccountRealm extends AuthorizingRealm { 052 053 /** 054 * username-to-SimpleAccount. 055 */ 056 protected final Map<String, SimpleAccount> users; 057 058 /** 059 * roleName-to-SimpleRole. 060 */ 061 protected final Map<String, SimpleRole> roles; 062 063 protected final ReadWriteLock usersLock; 064 protected final ReadWriteLock rolesLock; 065 066 public SimpleAccountRealm() { 067 this.users = new LinkedHashMap<String, SimpleAccount>(); 068 this.roles = new LinkedHashMap<String, SimpleRole>(); 069 usersLock = new ReentrantReadWriteLock(); 070 rolesLock = new ReentrantReadWriteLock(); 071 //SimpleAccountRealms are memory-only realms - no need for an additional cache mechanism since we're 072 //already as memory-efficient as we can be: 073 setCachingEnabled(false); 074 } 075 076 public SimpleAccountRealm(String name) { 077 this(); 078 setName(name); 079 } 080 081 protected SimpleAccount getUser(String username) { 082 usersLock.readLock().lock(); 083 try { 084 return this.users.get(username); 085 } finally { 086 usersLock.readLock().unlock(); 087 } 088 } 089 090 public boolean accountExists(String username) { 091 return getUser(username) != null; 092 } 093 094 public void addAccount(String username, String password) { 095 addAccount(username, password, (String[]) null); 096 } 097 098 public void addAccount(String username, String password, String... roles) { 099 Set<String> roleNames = CollectionUtils.asSet(roles); 100 SimpleAccount account = new SimpleAccount(username, password, getName(), roleNames, null); 101 add(account); 102 } 103 104 protected String getUsername(SimpleAccount account) { 105 return getUsername(account.getPrincipals()); 106 } 107 108 protected String getUsername(PrincipalCollection principals) { 109 return getAvailablePrincipal(principals).toString(); 110 } 111 112 protected void add(SimpleAccount account) { 113 String username = getUsername(account); 114 usersLock.writeLock().lock(); 115 try { 116 this.users.put(username, account); 117 } finally { 118 usersLock.writeLock().unlock(); 119 } 120 } 121 122 protected SimpleRole getRole(String rolename) { 123 rolesLock.readLock().lock(); 124 try { 125 return roles.get(rolename); 126 } finally { 127 rolesLock.readLock().unlock(); 128 } 129 } 130 131 public boolean roleExists(String name) { 132 return getRole(name) != null; 133 } 134 135 public void addRole(String name) { 136 add(new SimpleRole(name)); 137 } 138 139 protected void add(SimpleRole role) { 140 rolesLock.writeLock().lock(); 141 try { 142 roles.put(role.getName(), role); 143 } finally { 144 rolesLock.writeLock().unlock(); 145 } 146 } 147 148 protected static Set<String> toSet(String delimited, String delimiter) { 149 if (delimited == null || delimited.trim().equals("")) { 150 return null; 151 } 152 153 Set<String> values = new HashSet<String>(); 154 String[] rolenamesArray = delimited.split(delimiter); 155 for (String s : rolenamesArray) { 156 String trimmed = s.trim(); 157 if (trimmed.length() > 0) { 158 values.add(trimmed); 159 } 160 } 161 162 return values; 163 } 164 165 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 166 UsernamePasswordToken upToken = (UsernamePasswordToken) token; 167 SimpleAccount account = getUser(upToken.getUsername()); 168 169 if (account != null) { 170 171 if (account.isLocked()) { 172 throw new LockedAccountException("Account [" + account + "] is locked."); 173 } 174 if (account.isCredentialsExpired()) { 175 String msg = "The credentials for account [" + account + "] are expired"; 176 throw new ExpiredCredentialsException(msg); 177 } 178 179 } 180 181 return account; 182 } 183 184 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 185 String username = getUsername(principals); 186 usersLock.readLock().lock(); 187 try { 188 return this.users.get(username); 189 } finally { 190 usersLock.readLock().unlock(); 191 } 192 } 193}