1、shiro原理简析
原理简析:
1、subject支持不通调用获取用户信息
2、SecurityManager安全管理器继承了Authenticator(认证)、Authorizer(授权)、SessionManager、cacheManager(缓存)等,realms域支持不同的数据库做具体授权。
3、密码生成器通过cryptography
1、基础方法认证源码分析
目录结构.png
主要思路:
1、从配置文件获取设置的用户密码
2、校验输入的用户密码
3、验证结果输出
shiro的配置文件是ini类型的文件,简单定义几个账号密码,放在resource文件夹下
格式:# user1 = password, role1, role2, ...
[users]
xiaojin=666
lili=777
qiqi=888
测试代码
public class TestAuthenticator {
public static void main(String[] arg){
//1、创建安全管理器对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//2、给安全管理器设置realm
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
//3、工具类设置安全管理器
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("xiaoshi","888");
try{
subject.login(token);
System.out.println("认证成功");
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("不知名用户");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误");
}
}
用户认证校验是从login开始的,梳理下用户名认证流程如下:
认证流程.png
可以看出想自定义认证的化可以重写最后两个方法,其中SimpleAccountRealm:doGetAuthenticationInfo方法用来校验账号,密码是shiro自动认证的不需要我们修改
小结:AuthenticatingRealm认证realm 有doGetAuthenticationInfo方法;
AuthorizingRealm授权relam有doGetAuthorizationInfo方法;SimpleAccountRealm覆盖了这两个方法
2、spring实现认证
认证的基本思想:
1、通过过滤器拦截请求,获取用户信息
2、匹配数据库中认证/权限信息,可以添加其他功能,比如:密码添加加密认证信息等。
3、如果内容正确,允许访问,否则重新尝试验证或阻止访问
下面是重写的配置文件,
@Configuration
public class ShiroConfig {
//1、filter
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
Map<String,String> map = new HashMap<>();
//此类请求不拦截
map.put("/test/login","anon");
//此类请求需要鉴权
map.put("/test/logout","authc")
//通配符可以节省代码量
map.put("/sysUser/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
return shiroFilterFactoryBean;
}
//2、创建安全管理器
@Bean
public DefaultWebSecurityManager getDefauleWebSecurity( @Qualifier("realm") Realm realm){
//上文说的SecurityManager在spring里是DefaultWebSecurityManager
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
//3、创建自定义realm
@Bean
@Qualifier("realm")
public Realm getRealm(){
CustomerRealm customerRealm = new CustomerRealm();
// //修改凭证校验匹配器
// HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// hashedCredentialsMatcher.setHashAlgorithmName("MD5");
// //添加散列
// hashedCredentialsMatcher.setHashIterations(1024);
// customerRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return customerRealm;
}
}
对于授权来说用户和角色,角色和授权之间都是多对多的关系,权限下边又会分很多子粒度,重写授权和认证方法如下:
public class CustomerRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String primaryPrincipal = (String) principals.getPrimaryPrincipal();
//根据身份信息获取角色以及权限信息,这里的权限信息实际应该是数据库查询信息
if("lili".equals(primaryPrincipal)){
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//这里手动设置的用户具有admin权限,具体权限显示前端可以做角色控制(可以从数据库中查)
simpleAuthorizationInfo.addRole("user");
//权限设置:user角色可操作user:*:*粒度权限,需要结合前端标签做权限控制(可以从数据库中查)
simpleAuthorizationInfo.addStringPermission("user:*:*");
return simpleAuthorizationInfo;
}
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = (String) token.getPrincipal();
//根据身份信息获取密码信息,这里的用户信息实际应该是数据库查询信息
if("lili".equals(principal)){
return new SimpleAuthenticationInfo(principal ,"123",this.getName());
}
return null;
}
}
1、Principals(身份) 是Subject的“标识属性”,可以是任何与Subject相关的标识,比如说名称(给定名称)、名字(姓或者昵称)、用户名、安全号码等等,当然像昵称这样的内容不能很好的对Subject进行独特标识,所以最好的身份信息(Principals)是使用在程序中唯一的标识–典型的使用用户名或邮件地址。
Primary Principal(最主要的身份)虽然 Shiro 可以使用任何数量的身份,Shiro 还是希望一个程序精确地使用一个主要的身份–一个仅有的唯一标识 Subject 值。在多数程序中经常会是一个用户名、邮件地址或者全局唯一的用户 ID。
2、Credentials(证明) 通常是只有 Subject 知道的机密内容,用来证明他们真正拥有所需的身份,一些简单的证书例子如密码、指纹、眼底扫描和X.509证书等。
前端的权限控制,需要把html文件放在static文件夹里(用的是>2.0.3版本的spring,发现不支持jsp了,需要使用html文件)
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="utf-8" name="viewport"
content="width=device-width, initial-scale=1.0" http-equiv = "X-UA-Compatible" content ="ie=edge" />
<title>Docment</title>
</head>
<body>
<h1>系统主页V1.0</h1>
<!--<a href =${pageContext.request.contextPath}/test/login"> 退出用户</a>-->
<u1>
<shiro:hasAnyRoles name="admin,user">
<li><a href="" >用户管理</a></li>
<ul>
<shiro:hasPermission name=" user:add:*">
<li><a href="">添加</a> </li>
</shiro:hasPermission>
<shiro:hasPermission name=" user:delete:*">
<li><a href="">删除</a> </li>
</shiro:hasPermission>
<shiro:hasPermission name=" user:update:*">
<li><a href="">更新</a> </li>
</shiro:hasPermission>
</ul>
</shiro:hasAnyRoles>
<shiro:hasRole name="admin">
<li><a href="" >订单管理</a></li>
<li><a href="" >商品管理</a></li>
<li><a href="" >物流管理</a></li>
</shiro:hasRole>
</u1>
</body>
</html>
登录文件:
<html lang="en">
<head>
<meta charset="utf-8" name="viewport"
content="width=device-width, initial-scale=1.0" http-equiv = "X-UA-Compatible" content ="ie=edge" />
<title>Docment</title>
</head>
<body>
<h1>用户登录</h1>
<form action="./index.html" >
用户名:<input type="text" name="username"> <br/>
密码:<input type="text" name ="password"><br/>
<button type="submit">登录</button><button type="reset">取消</button>
</form>
</body>
</html>
此外还可以通过注解的方式进行角色校验 @RequiresRoles("角色"),如果是多个角色即 @RequiresRoles("角色1","角色2"),需要注意的是这里的意思是拥有角色1或者角色2的权限,CustomerRealm里也需要进行相应配置; 权限校验 @RequiresPermissions("user::")
3、缓存和过滤器
缓存支持:
1、ehcache管理
2、Redis集中管理
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
设置ehcache缓存
//3、创建自定义realm
@Bean
@Qualifier("realm")
public Realm getRealm(){
CustomerRealm customerRealm = new CustomerRealm();
//修改凭证校验匹配器
// HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// hashedCredentialsMatcher.setHashAlgorithmName("MD5");
// //添加散列
// hashedCredentialsMatcher.setHashIterations(1024);
// customerRealm.setCredentialsMatcher(hashedCredentialsMatcher);
customerRealm.setCacheManager(new EhCacheManager());
customerRealm.setCachingEnabled(true);
customerRealm.setAuthenticationCachingEnabled(true);
customerRealm.setAuthorizationCachingEnabled(true);
return customerRealm;
}
EhCacheManager继承了CacheManager返回的是Cache<K, V> ,如果想用redis缓存也可以继承CacheManager,定义redis的Cache<K, V>
持续更新中...
参考链接
1、官网十分钟入门-详情可以自己点点doc
2、授权
网友评论