shiro介绍
Apache Shiro 是 Java 的一个安全框架。目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Security 做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就好了
环境搭建
1 导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.2.2</version> </dependency>
<dependency> <groupId>com.github.theborakompanioni</groupId> <artifactId>thymeleaf-extras-shiro</artifactId> <version>2.0.0</version> </dependency
|
2 创建realm类,继承接口AuthorizingRealm
1
| public class MyRealm extends AuthorizingRealm
|
3 创建shiro配置类ShiroConfig ,并自行加上未经授权页面noPower.html和login.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| @Configuration public class ShiroConfig {
@Bean(name={"hashedCred"}) public HashedCredentialsMatcher getMatcher(){ HashedCredentialsMatcher hsc = new HashedCredentialsMatcher(); hsc.setHashAlgorithmName("MD5"); hsc.setHashIterations(1024); return hsc; }
@Bean(name={"myRealm"}) public MyRealm getRealm(@Qualifier("hashedCred") HashedCredentialsMatcher hs ){ MyRealm realm = new MyRealm(); realm.setCredentialsMatcher(hs); return realm; }
@Bean(name={"securityManager"}) public DefaultWebSecurityManager getSecurity(@Qualifier("myRealm") MyRealm realm){ DefaultWebSecurityManager dm = new DefaultWebSecurityManager(); dm.setRealm(realm); return dm; }
@Bean public LifecycleBeanPostProcessor getProcess(){ return new LifecycleBeanPostProcessor(); }
@Bean public ShiroDialect shiroDialect() { return new ShiroDialect(); }
@Bean public ShiroFilterFactoryBean getShiroFilter(@Qualifier("securityManager") DefaultWebSecurityManager sm){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(sm); bean.setLoginUrl("/login"); bean.setUnauthorizedUrl("/noPower"); return bean; } }
|
4 创建一个MD5的工具类,用来新增用户密码和修改用户密码 进行密码加密处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
@Component public class MdFive {
public String encrypt(String password,String saltValue){ Object salt = new Md5Hash(saltValue); Object result = new SimpleHash("MD5", password, salt, 1024);
return result+""; }
}
|
shiro认证
认证即是 身份验证,即在应用中谁能证明他就是他本人。一般提供如他们的身份 ID 一些标识信息来表明他就是他本人,如提供身份证,用户名 / 密码来证明。
在 shiro 中,用户需要提供 principals (身份)和 credentials(证明)给 shiro,从而应用能验证用户身份:
principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个 principals,但只有一个 Primary principals,一般是用户名 / 密码 / 手机号。
credentials:证明 / 凭证,即只有主体知道的安全值,如密码 / 数字证书等。
最常见的 principals 和 credentials 组合就是用户名 / 密码了。接下来先进行一个基本的身份认证。
实现用户密码MD5加密登录实现: 实现步骤如下:
1 Service层接口重新定义一个登录接口方法。
1
| HashMap<String,Object> loginShiro(UserInfo userInfo, HttpServletRequest request );
|
2 Service层接口实现类代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Override public HashMap<String, Object> loginShiro(UserInfo userInfo, HttpServletRequest request) { HashMap<String, Object> map = new HashMap<String, Object>(); UsernamePasswordToken token = new UsernamePasswordToken(userInfo.getUserName(),userInfo.getUserPwd()); try { sub.login(token); HttpSession session = request.getSession(); session.setAttribute("user",user); map.put("info",true); } catch(IncorrectCredentialsException ie){ System.err.println("密码不正确"); map.put("info","密码不正确"); }catch (UnknownAccountException e){ e.printStackTrace(); map.put("info","用户名输入错误"); } return map; }
|
3 实现myreal类的认证方法,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class MyRealm extends AuthorizingRealm { @Autowired private UserInfoService userInfoService; @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; }
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken myToken = (UsernamePasswordToken)token; String userName = myToken.getPrincipal()+"";
UserInfo userInfo =userInfoService.findByUserName(userName); if(userInfo==null){ throw new UnknownAccountException(); } AuthenticationInfo auth = new SimpleAuthenticationInfo( userInfo.getUserName(), userInfo.getUserPwd(),new Md5Hash(userName),this.getName());
return auth; } }
|
4 服务端代码完毕,前端页面简单这里就不贴了
5 与身份验证相关的过滤器
authc 表示访问该链接,必须要进行登录认证
例如 :/index.do=authc
anon 匿名过滤器,即不需要登录即可访问
例如:/login.jsp=anon
logout 退出过滤器,
注意:控制器无需定义logout.do地址,页面有这个地址就可以了
例如:/logout.do=logout
shiro授权
授权
授权,也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等)。在授权中需了解的几个关键对象:主体(Subject)、资源(Resource)、权限(Permission)、角色(Role)。
主体
主体,即访问应用的用户,在 Shiro 中使用 Subject 代表该用户。用户只有授权后才允许访问相应的资源。
资源
在应用中用户可以访问的任何东西,比如访问 JSP 页面、查看/编辑某些数据、访问某个业务方法、打印文本等等都是资源。用户只要授权后才能访问。
权限
权限代表了用户有没有操作某个资源的权利,即反映在某个资源上的操作允不允许,不反映谁去执行这个操作。所以后续还需要把权限赋予给用户,即定义哪个用户允许在某个资源上做什么操作(权限),Shiro 不会去做这件事情,而是由实现人员提供。
例如:查看/新增/修改/删除用户数据(即很多时候都是 CRUD(增查改删)式权限控制)
角色
角色代表了操作集合,可以理解为权限的集合,一般情况下我们会赋予用户角色而不是权限,即这样用户可以拥有一组权限,赋予权限时比较方便。典型的如:项目经理、技术总监、CTO、开发工程师等都是角色,不同的角色拥有一组不同的权限。
授权实现思路
在自定义的myreaml中doGetAuthorizationInfo 方法中实现
1 获取身份验证通过的用户名
1
| Object userName =principals.fromRealm(this.getName()).iterator().next();
|
2 根据用户名查询去数据库查询用户拥有的权限或者角色
1 2
| //查询拥有拥有的角色信息 UserInfo u= userInfoMapper.queryByName(userName);
|
3 创建shiro授权对象
1
| SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();
|
4 给用户赋予权限或者角色
1 2 3 4 5
| //给用户赋予角色
simpleAuthorInfo.addRoles(查询出来的当前用户角色集合); //给用户赋予权限 simpleAuthorInfo.addStringPermissions(查询出来的当前用户权限集合);
|
5 返回授权对象
1
| return simpleAuthorInfo;
|
6 完整授权代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String userName =principals.fromRealm(this.getName()).iterator().next()+""; UserInfo u= userInfoMapper.queryByName(userName); List<String> list = new ArrayList<>(); for(RoleInfo r:u.getRoleInfoList()){ list.add(r.getRemark()); System.err.println("给用户"+userName+"赋予了"+r.getRemark()+"角色"); } SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo(); auth.addRoles(list);
return auth; }
|
7 授权功能要实现需要配置 过滤器 或者 权限标签才能实现,配置过滤器代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Bean public ShiroFilterFactoryBean getShiroFilter(@Qualifier("securityManager") DefaultWebSecurityManager sm){ ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(sm); bean.setLoginUrl("/login"); bean.setUnauthorizedUrl("/noPower"); HashMap<String, String> map = new HashMap<String, String>();
map.put("/index", "authc"); map.put("/userInfo/list", "perms[userInforList]"); map.put("/userInfo/add", "perms[userInforAdd]"); map.put("/userInfo/Update", "roles[admin]"); bean.setFilterChainDefinitionMap(map); return bean; }
|
8 与授权相关的过滤器
perms权限授权过滤器,验证用户是否拥有所有权限
例如: /emp/filedownPage.do=perms[自定义权限参数]
Roles角色授权过滤器,验证用户是否拥有所有角色
例如: /emp/filedownPage.do=roles[自定义角色参数]
springboot中 shiro标签的使用
1 首先强调一下 角色和权限标签的使用和配置类中配置的角色和权限没有关系,只和reaml类中
的授权有关系
2 在要授权的html页面,把html标签改成
1 2 3
| <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
|
3 Shiro标签 在springboot中用法
权限标签写法 ,userInforList, roleInfoList 是自定义权限参数
一:只有拥有userInforList权限才能访问:
shiro:hasPermission=”userInforList”
二: 拥有userInforLis和roleInfoList 任意一个权限才能访问:shiro:hasAnyPermissions=”userInforList,roleInfoList”
三: 拥有userInforLis和roleInfoList 所有权限才能访问:
shiro:hasAllPermissions=”userInforList,roleInfoList”
四: 没有roleInfoList权限才能访问
shiro: lacksPermission =” roleInfoList “
角色标签写法 ,admin和vip 是自定义角色参数
一:只有拥有admin角色才能访问:
shiro:hasRole=”admin”
二: 拥有admin和vip任意一个角色才能访问:
shiro:hasAnyRoles=”admin,vip”
三: 拥有admin和vip 所有角色才能访问:
shiro:hasAllRoles=”admin,vip”
四: 没有admin 角色才能访问
shiro: lacksRole =”admin”
案例演示
用户标签写法
1
| shiro:principal="username"
|
例一:配置访问用户管理页面需要 用户拥有userInfoList权限,配置如下
1 2 3 4 5 6
| <li shiro:hasPermission="userInforList"> <a _href="/userInfo/list"> <i class="iconfont"></i> <cite>用户管理</cite> </a> </li >
|
例二:配置访问用户管理修改页面需要 用户拥有admin角色,配置如下
1 2 3
| <a shiro:hasRole="admin" title="编辑" onclick="update(this)" href="javascript:;"> <i class="layui-icon"></i> </a>
|
例三:配置访问拥有admin角色可以看到咨询,没有admin角色可以看到图片,配置如下
1 2 3 4 5 6
| <dd shiro:hasRole="admin"> <a onclick="x_admin_show('资讯','http://www.baidu.com')"><i class="iconfont"></i>资讯</a> </dd> <dd shiro:lacksRole="admin" > <a onclick="x_admin_show('图片','http://www.baidu.com')"><i class="iconfont"></i>图片</a> </dd>
|