文章目錄
- 前言
- 一、shiro簡介
- 二、環境搭建
- 2.1.數據庫
- 2.1.1user用戶表
- 2.1.2user_role用戶角色關系表
- 2.1.3role角色表
- 2.1.4role_permission角色權限關系表
- 2.1.5permission權限表
- 2.2導坐標
- 2.3實體類
- 2.3.1User
- 2.3.2Role
- 2.3.3Permission
- 2.4MVC三層
- 2.4.1User
- 2.4.1.1mapper層
- 2.4.1.2service層
- 2.4.1.3controller層
- 2.4.2Role
- 2.4.2.1mapper層
- 2.4.2.2service層
- 2.4.2.3controller層
- 2.3.3Permission
- 2.4.3.1mapper層
- 2.4.3.2service層
- 2.4.3.3controller層
- 2.5.加密工具類
- 2.6.全局異常處理器
- 2.7自定義Realm
- 2.8shiro配置類
- 三、案例演示
- 3.1 登錄認證
- 3.2 權限認證
- 總結
前言
近期對Springboot框架的學習中,為了更好的學習理解Springsecurity中間件,先學習了一下“老派”的shiro安全框架,本文章將通過注解的方式實現基礎的用戶認證和角色授權案例
一、shiro簡介
Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、授權、密碼和會話管理。使用Shiro的易于理解的API,您可以快速、輕松地獲得任何應用程序,從最小的移動應用程序到最大的網絡和企業應用程序。
二、環境搭建
2.1.數據庫
2.1.1user用戶表
2.1.2user_role用戶角色關系表
2.1.3role角色表
2.1.4role_permission角色權限關系表
2.1.5permission權限表
2.2導坐標
<properties><java.version>1.8</java.version><!--shiro--><shiro.version>1.3.2</shiro.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-logging</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.29</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3</version></dependency><!-- SECURITY begin --><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>${shiro.version}</version></dependency><!-- SECURITY end --></dependencies>
2.3實體類
2.3.1User
@TableName("pe_user")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {@TableId(value = "id",type = IdType.AUTO)private int id;private String username;private String password;private String salt;@TableField(exist = false)private Set<Role> roles;
}
2.3.2Role
@TableName("pe_role")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Role {@TableId(value = "id",type = IdType.AUTO)private int id;private String name;private String code;private String description;@TableField(exist = false)private Set<Permission> permissions;
}
2.3.3Permission
@TableName("pe_permission")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Permission {@TableId(value = "id",type = IdType.AUTO)private int id;private String name;private String code;private String description;
}
2.4MVC三層
2.4.1User
2.4.1.1mapper層
public interface UserMapper extends BaseMapper<User> {@Select("select * from pe_user where username = #{name}")User findUserByName(String name);//級聯查詢@Results({@Result(column = "id",property = "id"),@Result(column = "username",property = "username"),@Result(column = "password",property = "password"),@Result(column = "salt",property = "salt"),@Result(column = "id",property = "roles",many=@Many(select = "com.apesource.shirostudy_demo_05.dao.RoleMapper.findRoleIdsByUserId"))})@Select("select * from pe_user where id = #{id}")User findUserDetailById(@Param("id") int id);
}
2.4.1.2service層
public interface IUserService extends IService<User> {User findUserByName(String name);User findUserDetailById(int id);
}@Service
@SuppressWarnings("all")
public class UserServiceImp extends ServiceImpl<UserMapper, User> implements IUserService {@AutowiredUserMapper userMapper;@AutowiredIRoleService roleService;@Overridepublic User findUserByName(String name) {User user = userMapper.findUserByName(name);return user;}@Overridepublic User findUserDetailById(int id) {//根據userid查詢userUser user = userMapper.findUserDetailById(id);//返回return user;}
}
2.4.1.3controller層
@RestController
public class UserController {@Autowiredprivate IUserService userService;//個人主頁@RequiresPermissions("user-home")@RequestMapping(value = "/user/home")public String home() {return "訪問個人主頁成功";}//根據名字查詢用戶基本信息@RequiresPermissions("user-find")@RequestMapping(value = "/user/userByName/{name}")public String findUserByName(@PathVariable String name) {System.out.println(name);User user = userService.findUserByName(name);return user.toString();}//更新@RequiresPermissions("user-update")@RequestMapping(value = "/user/{id}", method = RequestMethod.PUT)public String update(@PathVariable String id) {return "更新用戶成功";}//刪除@RequiresPermissions("user-delete")@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)public String delete(@PathVariable String id) {return "刪除用戶成功";}//根據id查詢用戶詳細信息@RequiresPermissions("user-find")@RequestMapping(value = "/user/userById/{id}")public String findUserDetailById(@PathVariable int id) {return userService.findUserDetailById(id).toString();}//登錄頁面@RequestMapping(value = "/login")public String login(User user) {try {//1.構造登錄令牌UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());//2.//2.獲取subjectSubject subject = SecurityUtils.getSubject();//3.調用subject進行登錄subject.login(token);return "登錄成功!!!";} catch (AuthenticationException e) {return "用戶名或密碼錯誤!!!";}}//未登陸與未授權頁面@RequestMapping(value = "/autherror")public String autherror() {return "未登錄";}}
2.4.2Role
2.4.2.1mapper層
public interface RoleMapper extends BaseMapper<Role> {@Results({@Result(column = "id",property = "id"),@Result(column = "name",property = "name"),@Result(column = "code",property = "code"),@Result(column = "description",property = "description"),@Result(column = "id",property = "permissions",many =@Many(select = "com.apesource.shirostudy_demo_05.dao.PermissionMapper.findPermissionIdsByRoleId"))})@Select("SELECT * FROM pe_role WHERE id IN (SELECT role_id FROM pe_user_role WHERE user_id = #{id})")Set<Role> findRoleIdsByUserId(int id);
}
2.4.2.2service層
public interface IRoleService extends IService<Role> {Set<Role> findRoleIdsByUserId(int id);
}
@Service
@SuppressWarnings("all")
public class RoleServiceImp extends ServiceImpl<RoleMapper, Role> implements IRoleService {@AutowiredRoleMapper roleMapper;@AutowiredIPermissionService permissionService;@Overridepublic Set<Role> findRoleIdsByUserId(int id) {Set<Role> roles = roleMapper.findRoleIdsByUserId(id);return roles;}
}
2.4.2.3controller層
由于不演示,無。
2.3.3Permission
2.4.3.1mapper層
public interface PermissionMapper extends BaseMapper<Permission> {@Results({@Result(column = "id",property = "id"),@Result(column = "name",property = "name"),@Result(column = "code",property = "code"),@Result(column = "description",property = "description"),})@Select("SELECT * FROM pe_permission WHERE id IN (SELECT permission_id FROM pe_role_permission WHERE role_id = #{id} ) ")Set<Permission> findPermissionIdsByRoleId(int id);
}
2.4.3.2service層
public interface IPermissionService extends IService<Permission> {Set<Permission> findPermissionIdsByRoleId(int id);
}
@Service
public class PermissionServiceImp extends ServiceImpl<PermissionMapper, Permission> implements IPermissionService {@Autowired(required = false)PermissionMapper permissionMapper;@Overridepublic Set<Permission> findPermissionIdsByRoleId(int id) {Set<Permission> permissionIds = permissionMapper.findPermissionIdsByRoleId(id);return permissionIds;}
}
2.4.3.3controller層
由于不演示,無。
2.5.加密工具類
public class DigestsUtil {public static final String SHA1 = "SHA-1";public static final Integer COUNTS =369;/*** @Description sha1方法* @param input 需要散列字符串* @param salt 鹽字符串* @return*/public static String show(String input, String salt) {return new SimpleHash(SHA1, input, salt,COUNTS).toString();}/*** @Description 隨機獲得salt字符串* @return*/public static String generateSalt(){SecureRandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();return randomNumberGenerator.nextBytes().toHex();}/*** @Description 生成密碼字符密文和salt密文* @param* @return*/public static Map<String,String> entryptPassword(String passwordPlain) {Map<String,String> map = new HashMap<>();String salt = generateSalt();String password =show(passwordPlain,salt);map.put("salt", salt);map.put("password", password);return map;}
}
2.6.全局異常處理器
@ControllerAdvice
public class BaseExceptionHandler {@ExceptionHandler(value = AuthorizationException.class)@ResponseBodypublic String error(HttpServletRequest request, HttpServletResponse response, AuthorizationException e){return "未授權!!!";}
}
2.7自定義Realm
public class MyRealm extends AuthorizingRealm {@AutowiredIUserService userService;@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {int userId = (int) principalCollection.getPrimaryPrincipal();User user = userService.findUserDetailById(userId);Set<String> roles = new HashSet<>();Set<String> permissions = new HashSet<>();user.getRoles().stream().forEach(role -> {roles.add(role.getCode());role.getPermissions().stream().forEach(permission -> {permissions.add(permission.getCode());});});SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.addRoles(roles);info.addStringPermissions(permissions);return info;}/*** 認證方法* 參數:傳遞的用戶名密碼*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {//1.獲取登錄的用戶名密碼(token)UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;String username = token.getUsername();//2.根據用戶名查詢數據庫//mybatis情景下:user對象中包含ID,name,pwd(匿名)User user = userService.findUserByName(username);if(user != null){SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user.getId(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),"myRealm");return info;}return null;}/*** @Description 自定義密碼比較器* @param* @return* bean標簽 init-method屬性*/@PostConstructpublic void initCredentialsMatcher() {//指定密碼算法HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);//指定迭代次數hashedCredentialsMatcher.setHashIterations(DigestsUtil.COUNTS);//生效密碼比較器setCredentialsMatcher(hashedCredentialsMatcher);}
}
2.8shiro配置類
@Configuration
public class ShiroConfiguration {/*** 1.創建shiro自帶cookie對象*/@Beanpublic SimpleCookie sessionIdCookie(){SimpleCookie simpleCookie = new SimpleCookie();simpleCookie.setName("ShiroSession");return simpleCookie;}//2.創建realm@Beanpublic MyRealm getRealm(){return new MyRealm();}/*** 3.創建會話管理器*/@Beanpublic DefaultWebSessionManager sessionManager(){DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();sessionManager.setSessionValidationSchedulerEnabled(false);sessionManager.setSessionIdCookieEnabled(true);sessionManager.setSessionIdCookie(sessionIdCookie());sessionManager.setGlobalSessionTimeout(3600000);return sessionManager;}//4.創建安全管理器@Beanpublic SecurityManager defaultWebSecurityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(getRealm());securityManager.setSessionManager(sessionManager());return securityManager;}/*** 5.保證實現了Shiro內部lifecycle函數的bean執行*/@Bean(name = "lifecycleBeanPostProcessor")public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 6.開啟對shiro注解的支持* AOP式方法級權限檢查*/@Bean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}/*** 7.配合DefaultAdvisorAutoProxyCreator事項注解權限校驗*/@Beanpublic AuthorizationAttributeSourceAdvisor getAuthorizationAttributeSourceAdvisor() {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager());return authorizationAttributeSourceAdvisor;}//8.配置shiro的過濾器工廠再web程序中,shiro進行權限控制全部是通過一組過濾器集合進行控制@Beanpublic ShiroFilterFactoryBean shiroFilter() {//1.創建過濾器工廠ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();//2.設置安全管理器filterFactory.setSecurityManager(defaultWebSecurityManager());//3.通用配置(跳轉登錄頁面,為授權跳轉的頁面)filterFactory.setLoginUrl("/autherror");//跳轉url地址//4.設置過濾器集合//key = 攔截的url地址//value = 過濾器類型Map<String,String> filterMap = new LinkedHashMap<>();//key:請求規則 value:過濾器名稱filterMap.put("/login","anon");//當前請求地址可以匿名訪問filterMap.put("/user/**","authc");//當前請求地址必須認證之后可以訪問//在過濾器工程內設置系統過濾器filterFactory.setFilterChainDefinitionMap(filterMap);return filterFactory;}}
三、案例演示
用戶密碼均是123456加密后保存至數據庫
3.1 登錄認證
成功登錄:
失敗登錄:
3.2 權限認證
由于剛剛登錄的信息有管理員和員工的身份,所以其所有功能都能使用。
有權限時:
無權限時:
無權限時會被全局異常處理器攔截,并作出響應的響應。
總結
通過對shrio的學習,更好的理解了安全認證以及鑒權授權的流程,大概了解了安全認證的機制,對我之后學習理解Springsecurity有更好的幫助。