原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398
根据下载的pdf学习。
第六章 Realm及相关对象
1.用户、角色、权限的关系
用户和角色是多对多,角色和权限也是多对多。
用户和权限通过角色建立关系。
角色是权限的集合。
(1)表准备
用户表:
角色表:
剩下的表类似,就不一一解释了。分别是权限表,用户-角色表,角色权限表。
(2)表对应的实体
2.环境准备
(1)pom.xml
3.Service和Dao层
(1)Service层
1 public interface PermissionService {
2 public Permission createPermission(Permission permission);
3 public void deletePermission(Long permissionId);
4 }
public interface RoleService {
public Role createRole(Role role);
public void deleteRole(Long roleId); /**
* 添加角色-权限之间关系
* @param roleId
* @param permissionIds
*/
public void correlationPermissions(Long roleId, Long... permissionIds); /**
* 移除角色-权限之间关系
* @param roleId
* @param permissionIds
*/
public void uncorrelationPermissions(Long roleId, Long... permissionIds); }
public interface UserService {
/**
* 创建用户
* @param user
*/
public User createUser(User user); /**
* 修改密码
* @param userId
* @param newPassword
*/
public void changePassword(Long userId, String newPassword); /**
* 添加用户-角色关系
* @param userId
* @param roleIds
*/
public void correlationRoles(Long userId, Long... roleIds); /**
* 移除用户-角色关系
* @param userId
* @param roleIds
*/
public void uncorrelationRoles(Long userId, Long... roleIds); /**
* 根据用户名查找用户
* @param username
* @return
*/
public User findByUsername(String username); /**
* 根据用户名查找其角色
* @param username
* @return
*/
public Set<String> findRoles(String username); /**
* 根据用户名查找其权限
* @param username
* @return
*/
public Set<String> findPermissions(String username); }
(2)Service实现层
public class PermissionServiceImpl implements PermissionService { private PermissionDao permissionDao = new PermissionDaoImpl(); public Permission createPermission(Permission permission) {
return permissionDao.createPermission(permission);
} public void deletePermission(Long permissionId) {
permissionDao.deletePermission(permissionId);
}
}
public class RoleServiceImpl implements RoleService { private RoleDao roleDao = new RoleDaoImpl(); public Role createRole(Role role) {
return roleDao.createRole(role);
} public void deleteRole(Long roleId) {
roleDao.deleteRole(roleId);
} /**
* 添加角色-权限之间关系
* @param roleId
* @param permissionIds
*/
public void correlationPermissions(Long roleId, Long... permissionIds) {
roleDao.correlationPermissions(roleId, permissionIds);
} /**
* 移除角色-权限之间关系
* @param roleId
* @param permissionIds
*/
public void uncorrelationPermissions(Long roleId, Long... permissionIds) {
roleDao.uncorrelationPermissions(roleId, permissionIds);
} }
public class UserServiceImpl implements UserService { private UserDao userDao = new UserDaoImpl();
private PasswordHelper passwordHelper = new PasswordHelper(); /**
* 创建用户
* @param user
*/
public User createUser(User user) {
//加密密码
passwordHelper.encryptPassword(user);
return userDao.createUser(user);
} /**
* 修改密码
* @param userId
* @param newPassword
*/
public void changePassword(Long userId, String newPassword) {
User user =userDao.findOne(userId);
user.setPassword(newPassword);
passwordHelper.encryptPassword(user);
userDao.updateUser(user);
} /**
* 添加用户-角色关系
* @param userId
* @param roleIds
*/
public void correlationRoles(Long userId, Long... roleIds) {
userDao.correlationRoles(userId, roleIds);
} /**
* 移除用户-角色关系
* @param userId
* @param roleIds
*/
public void uncorrelationRoles(Long userId, Long... roleIds) {
userDao.uncorrelationRoles(userId, roleIds);
} /**
* 根据用户名查找用户
* @param username
* @return
*/
public User findByUsername(String username) {
return userDao.findByUsername(username);
} /**
* 根据用户名查找其角色
* @param username
* @return
*/
public Set<String> findRoles(String username) {
return userDao.findRoles(username);
} /**
* 根据用户名查找其权限
* @param username
* @return
*/
public Set<String> findPermissions(String username) {
return userDao.findPermissions(username);
} }
import com.github.zhangkaitao.shiro.chapter6.entity.User;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource; public class PasswordHelper { private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator(); private String algorithmName = "md5";
private final int hashIterations = 2; public void encryptPassword(User user) { user.setSalt(randomNumberGenerator.nextBytes().toHex()); String newPassword = new SimpleHash(
algorithmName,
user.getPassword(),
ByteSource.Util.bytes(user.getCredentialsSalt()),
hashIterations).toHex(); user.setPassword(newPassword);
}
}
(3)Dao层
public interface PermissionDao {
public Permission createPermission(Permission permission);
public void deletePermission(Long permissionId);
}
public interface RoleDao { public Role createRole(Role role);
public void deleteRole(Long roleId); public void correlationPermissions(Long roleId, Long... permissionIds);
public void uncorrelationPermissions(Long roleId, Long... permissionIds); }
public interface UserDao { public User createUser(User user);
public void updateUser(User user);
public void deleteUser(Long userId); public void correlationRoles(Long userId, Long... roleIds);
public void uncorrelationRoles(Long userId, Long... roleIds); User findOne(Long userId);
User findByUsername(String username); Set<String> findRoles(String username);
Set<String> findPermissions(String username);
}
(4)dao实现层
import com.github.zhangkaitao.shiro.chapter6.JdbcTemplateUtils;
import com.github.zhangkaitao.shiro.chapter6.entity.Permission;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException; public class PermissionDaoImpl implements PermissionDao { private JdbcTemplate jdbcTemplate = JdbcTemplateUtils.jdbcTemplate(); public Permission createPermission(final Permission permission) {
final String sql = "insert into sys_permissions(permission, description, available) values(?,?,?)"; GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement psst = connection.prepareStatement(sql, new String[] { "id" });
psst.setString(1, permission.getPermission());
psst.setString(2, permission.getDescription());
psst.setBoolean(3, permission.getAvailable());
return psst;
}
}, keyHolder);
permission.setId(keyHolder.getKey().longValue()); return permission;
} public void deletePermission(Long permissionId) {
//首先把与permission关联的相关表的数据删掉
String sql = "delete from sys_roles_permissions where permission_id=?";
jdbcTemplate.update(sql, permissionId); sql = "delete from sys_permissions where id=?";
jdbcTemplate.update(sql, permissionId);
} }
import com.github.zhangkaitao.shiro.chapter6.JdbcTemplateUtils;
import com.github.zhangkaitao.shiro.chapter6.entity.Role;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException; public class RoleDaoImpl implements RoleDao { private JdbcTemplate jdbcTemplate = JdbcTemplateUtils.jdbcTemplate(); public Role createRole(final Role Role) {
final String sql = "insert into sys_roles(role, description, available) values(?,?,?)"; GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement psst = connection.prepareStatement(sql, new String[] { "id" });
psst.setString(1, Role.getRole());
psst.setString(2, Role.getDescription());
psst.setBoolean(3, Role.getAvailable());
return psst;
}
}, keyHolder);
Role.setId(keyHolder.getKey().longValue()); return Role;
} public void deleteRole(Long roleId) {
//首先把和role关联的相关表数据删掉
String sql = "delete from sys_users_roles where role_id=?";
jdbcTemplate.update(sql, roleId); sql = "delete from sys_roles where id=?";
jdbcTemplate.update(sql, roleId);
} @Override
public void correlationPermissions(Long roleId, Long... permissionIds) {
if(permissionIds == null || permissionIds.length == 0) {
return;
}
String sql = "insert into sys_roles_permissions(role_id, permission_id) values(?,?)";
for(Long permissionId : permissionIds) {
if(!exists(roleId, permissionId)) {
jdbcTemplate.update(sql, roleId, permissionId);
}
}
} @Override
public void uncorrelationPermissions(Long roleId, Long... permissionIds) {
if(permissionIds == null || permissionIds.length == 0) {
return;
}
String sql = "delete from sys_roles_permissions where role_id=? and permission_id=?";
for(Long permissionId : permissionIds) {
if(exists(roleId, permissionId)) {
jdbcTemplate.update(sql, roleId, permissionId);
}
}
} private boolean exists(Long roleId, Long permissionId) {
String sql = "select count(1) from sys_roles_permissions where role_id=? and permission_id=?";
return jdbcTemplate.queryForObject(sql, Integer.class, roleId, permissionId) != 0;
} }
import com.github.zhangkaitao.shiro.chapter6.JdbcTemplateUtils;
import com.github.zhangkaitao.shiro.chapter6.entity.User;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashSet;
import java.util.List;
import java.util.Set; public class UserDaoImpl implements UserDao { private JdbcTemplate jdbcTemplate = JdbcTemplateUtils.jdbcTemplate(); public User createUser(final User user) {
final String sql = "insert into sys_users(username, password, salt, locked) values(?,?,?, ?)"; GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement psst = connection.prepareStatement(sql, new String[] { "id" });
psst.setString(1, user.getUsername());
psst.setString(2, user.getPassword());
psst.setString(3, user.getSalt());
psst.setBoolean(4, user.getLocked());
return psst;
}
}, keyHolder); user.setId(keyHolder.getKey().longValue());
return user;
} public void updateUser(User user) {
String sql = "update sys_users set username=?, password=?, salt=?, locked=? where id=?";
jdbcTemplate.update(sql, user.getUsername(), user.getPassword(), user.getSalt(), user.getLocked(), user.getId());
} public void deleteUser(Long userId) {
String sql = "delete from sys_users where id=?";
jdbcTemplate.update(sql, userId);
} @Override
public void correlationRoles(Long userId, Long... roleIds) {
if(roleIds == null || roleIds.length == 0) {
return;
}
String sql = "insert into sys_users_roles(user_id, role_id) values(?,?)";
for(Long roleId : roleIds) {
if(!exists(userId, roleId)) {
jdbcTemplate.update(sql, userId, roleId);
}
}
} @Override
public void uncorrelationRoles(Long userId, Long... roleIds) {
if(roleIds == null || roleIds.length == 0) {
return;
}
String sql = "delete from sys_users_roles where user_id=? and role_id=?";
for(Long roleId : roleIds) {
if(exists(userId, roleId)) {
jdbcTemplate.update(sql, userId, roleId);
}
}
} private boolean exists(Long userId, Long roleId) {
String sql = "select count(1) from sys_users_roles where user_id=? and role_id=?";
return jdbcTemplate.queryForObject(sql, Integer.class, userId, roleId) != 0;
} @Override
public User findOne(Long userId) {
String sql = "select id, username, password, salt, locked from sys_users where id=?";
List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class), userId);
if(userList.size() == 0) {
return null;
}
return userList.get(0);
} @Override
public User findByUsername(String username) {
String sql = "select id, username, password, salt, locked from sys_users where username=?";
List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class), username);
if(userList.size() == 0) {
return null;
}
return userList.get(0);
} @Override
public Set<String> findRoles(String username) {
String sql = "select role from sys_users u, sys_roles r,sys_users_roles ur where u.username=? and u.id=ur.user_id and r.id=ur.role_id";
return new HashSet(jdbcTemplate.queryForList(sql, String.class, username));
} @Override
public Set<String> findPermissions(String username) {
//TODO 此处可以优化,比如查询到role后,一起获取roleId,然后直接根据roleId获取即可
String sql = "select permission from sys_users u, sys_roles r, sys_permissions p, sys_users_roles ur, sys_roles_permissions rp where u.username=? and u.id=ur.user_id and r.id=ur.role_id and r.id=rp.role_id and p.id=rp.permission_id";
return new HashSet(jdbcTemplate.queryForList(sql, String.class, username));
}
}
4.Realm
(1)配置
(2)代码
import com.github.zhangkaitao.shiro.chapter6.service.UserService;
import com.github.zhangkaitao.shiro.chapter6.service.UserServiceImpl;
import com.github.zhangkaitao.shiro.chapter6.entity.User;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource; public class UserRealm extends AuthorizingRealm { private UserService userService = new UserServiceImpl(); @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//授权
String username = (String)principals.getPrimaryPrincipal(); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(userService.findRoles(username));
authorizationInfo.setStringPermissions(userService.findPermissions(username)); return authorizationInfo;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//认证
String username = (String)token.getPrincipal();
User user = userService.findByUsername(username); if(user == null) {
throw new UnknownAccountException();//没找到帐号
} if(Boolean.TRUE.equals(user.getLocked())) {
throw new LockedAccountException(); //帐号锁定
} //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
user.getUsername(), //用户名
user.getPassword(), //密码
ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt
getName() //realm name
);
return authenticationInfo;
}
}
5.测试
包含这几种情况:登录成功、用户名错误、密码错误、密码超出重试次数、有/没有角色、有/没有权限的测试。
(1)测试基类
import com.github.zhangkaitao.shiro.chapter6.service.*;
import com.github.zhangkaitao.shiro.chapter6.entity.Permission;
import com.github.zhangkaitao.shiro.chapter6.entity.Role;
import com.github.zhangkaitao.shiro.chapter6.entity.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.apache.shiro.util.ThreadContext;
import org.junit.After;
import org.junit.Before; public abstract class BaseTest { protected PermissionService permissionService = new PermissionServiceImpl();
protected RoleService roleService = new RoleServiceImpl();
protected UserService userService = new UserServiceImpl(); protected String password = "123"; protected Permission p1;
protected Permission p2;
protected Permission p3;
protected Role r1;
protected Role r2;
protected User u1;
protected User u2;
protected User u3;
protected User u4; @Before
public void setUp() {
JdbcTemplateUtils.jdbcTemplate().update("delete from sys_users");
JdbcTemplateUtils.jdbcTemplate().update("delete from sys_roles");
JdbcTemplateUtils.jdbcTemplate().update("delete from sys_permissions");
JdbcTemplateUtils.jdbcTemplate().update("delete from sys_users_roles");
JdbcTemplateUtils.jdbcTemplate().update("delete from sys_roles_permissions"); //1、新增权限
p1 = new Permission("user:create", "用户模块新增", Boolean.TRUE);
p2 = new Permission("user:update", "用户模块修改", Boolean.TRUE);
p3 = new Permission("menu:create", "菜单模块新增", Boolean.TRUE);
permissionService.createPermission(p1);
permissionService.createPermission(p2);
permissionService.createPermission(p3);
//2、新增角色
r1 = new Role("admin", "管理员", Boolean.TRUE);
r2 = new Role("user", "用户管理员", Boolean.TRUE);
roleService.createRole(r1);
roleService.createRole(r2);
//3、关联角色-权限
roleService.correlationPermissions(r1.getId(), p1.getId());
roleService.correlationPermissions(r1.getId(), p2.getId());
roleService.correlationPermissions(r1.getId(), p3.getId()); roleService.correlationPermissions(r2.getId(), p1.getId());
roleService.correlationPermissions(r2.getId(), p2.getId()); //4、新增用户
u1 = new User("zhang", password);
u2 = new User("li", password);
u3 = new User("wu", password);
u4 = new User("wang", password);
u4.setLocked(Boolean.TRUE);
userService.createUser(u1);
userService.createUser(u2);
userService.createUser(u3);
userService.createUser(u4);
//5、关联用户-角色
userService.correlationRoles(u1.getId(), r1.getId()); } @After
public void tearDown() throws Exception {
ThreadContext.unbindSubject();//退出时请解除绑定Subject到线程 否则对下次测试造成影响
} protected void login(String configFile, String username, String password) {
//1、获取SecurityManager工厂,此处使用Ini配置文件初始化SecurityManager
Factory<org.apache.shiro.mgt.SecurityManager> factory =
new IniSecurityManagerFactory(configFile); //2、得到SecurityManager实例 并绑定给SecurityUtils
org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager); //3、得到Subject及创建用户名/密码身份验证Token(即用户身份/凭证)
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password); subject.login(token);
} public Subject subject() {
return SecurityUtils.getSubject();
} }
(2)测试类
import com.github.zhangkaitao.shiro.chapter6.BaseTest;
import junit.framework.Assert;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.junit.Test; public class UserRealmTest extends BaseTest { @Test
public void testLoginSuccess() {
login("classpath:shiro.ini", u1.getUsername(), password);
Assert.assertTrue(subject().isAuthenticated());
} @Test(expected = UnknownAccountException.class)
public void testLoginFailWithUnknownUsername() {
login("classpath:shiro.ini", u1.getUsername() + "1", password);
} @Test(expected = IncorrectCredentialsException.class)
public void testLoginFailWithErrorPassowrd() {
login("classpath:shiro.ini", u1.getUsername(), password + "1");
} @Test(expected = LockedAccountException.class)
public void testLoginFailWithLocked() {
login("classpath:shiro.ini", u4.getUsername(), password + "1");
} @Test(expected = ExcessiveAttemptsException.class)
public void testLoginFailWithLimitRetryCount() {
for(int i = 1; i <= 5; i++) {
try {
login("classpath:shiro.ini", u3.getUsername(), password + "1");
} catch (Exception e) {/*ignore*/}
}
login("classpath:shiro.ini", u3.getUsername(), password + "1"); //需要清空缓存,否则后续的执行就会遇到问题(或者使用一个全新账户测试)
} @Test
public void testHasRole() {
login("classpath:shiro.ini", u1.getUsername(), password );
Assert.assertTrue(subject().hasRole("admin"));
} @Test
public void testNoRole() {
login("classpath:shiro.ini", u2.getUsername(), password);
Assert.assertFalse(subject().hasRole("admin"));
} @Test
public void testHasPermission() {
login("classpath:shiro.ini", u1.getUsername(), password);
Assert.assertTrue(subject().isPermittedAll("user:create", "menu:create"));
} @Test
public void testNoPermission() {
login("classpath:shiro.ini", u2.getUsername(), password);
Assert.assertFalse(subject().isPermitted("user:create"));
} }