2017.2.7 开涛shiro教程-第六章-Realm及相关对象(一)
原博客地址: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")); } }
fighting for this