Shiro(Springboot版)
Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序
1.Shiro的三大核心API
1.1 Subject:用户主体,把操作交给SecurityManager
1.2 SecurityManager:安全管理器,与Realm相关联
1. 3 Realm:Shiro数据连接的桥梁,保证数据访问控制,用于身份验证与权限获取,这也是shiro的主要功能
2.添加依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
3.Controller
之前都是从前台获取username与password来进行数据库验证,然后判断是不是相同,但是现在是运用shiro验证
Subject subject=SecurityUtils.getSybject();
<!--定义一个令牌,可以理解为登录的凭证-->
<!--将获得的用户名和密码放进去-->
UsernamePasswordToken token=new UsernamePasswordToken(username,password);
<!--进行登录验证,需要进行异常处理-->
try {
subject.login(token);
} catch (AuthenticationException e) {
e.printStackTrace();
return "redirect:/mangae/login";
}
4.自定义一个Realm
既然进行了登录,那么就到了realm登场了,shiro如何验证与授权接下来你就懂了
4.1.自己做一个realm,必须继承AuthorizingRealm类,继承后便可生成两个方法
public class Realm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
生成的方法如上图可见
AuthenticationInfo 便是认证,也就是来判断用户登录是否成功
AuthorizationInfo 便是授权,是通过数据库来查看用户有的权限,并且存入,好在之后访问页面时候来判断是否有该权限
4.2 AuthenticationInfo 方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//这里便是我们从subject中运用.login传来的令牌,从中取出用户名
UsernamePasswordToken userToken= (UsernamePasswordToken)authenticationToken;
String username=userToken.getUsername();
//这便是通过调用service中方法查出是这个username的对象
Manager manger= manageService.findByUsername(username);
//这里是shiro还带了盐的功能,进行加密
Config config = configService.findByKey("salt");
String salt = config.getValue();
if (manger==null){
return null;//抛出异常 UnknownAccountException
}
//这里便是进行验证的操作,在这里面会进行验证传来的密码与数据库获得的是否一致
//第一个传的最好是对象,以便于验证时候使用,可以获取到
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
manger, //用户
manger.getPassword(), //密码
ByteSource.Util.bytes(salt), //盐
""
);
return authenticationInfo;
}
4.3 AuthenticationInfo方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//这里获得到的就是authenticationInfo中的第一个变量
Manager manager=(Manager)principalCollection.getPrimaryPrincipal();
//设置角色字符串及权限字符串
Set<String> roles = new HashSet<>();
Set<String> perms = new HashSet<>();
Set<Role> roleSet = admin.getRoles();
roleSet.forEach(role -> { roles.add(role.getRole());
role.getPermissions().forEach(permission -> perms.add(permission.getPerm())); });
//设置角色字符串
info.setRoles(roles);
//设置权限字符串
info.setStringPermissions(perms);
return info;
}
通俗来说,这一步就是查出来你所拥有的角色以及你所拥有的权限,在之后的ShiroFilterFactoryBean中来判断你是否有访问某些路径的权限
4.4 贴上完整代码
public class Realm extends AuthorizingRealm {
@Resource
private ManageService manageService;
@Resource
private PermissionService permissionService;
@Resource
private ConfigService configService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
Manager manager=(Manager)principalCollection.getPrimaryPrincipal();
//查询角色字符串集合
Set<String> roles = new HashSet<>();
//查询权限字符串集合
Set<String> permissions = new HashSet<>();
Set<Role> roleSet = admin.getRoles();
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//从数据库读出 所拥有的权限 显示出来
roleSet.forEach(role -> { roles.add(role.getRole());
role.getPermissions().forEach(permission ->
perms.add(permission.getPerm())); });
//将拥有的权限和角色存入
info.setRoles(roles);
info.setStringPermissions(permissions);
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken userToken= (UsernamePasswordToken)authenticationToken;
String username=userToken.getUsername();
System.out.println(username);
Manager manger= manageService.findByUsername(username);
Config config = configService.findByKey("salt");
String salt = config.getValue();
if (manger==null){
return null;//抛出异常 UnknownAccountException
}
//验证
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
manger, //用户名
manger.getPassword(), //密码
ByteSource.Util.bytes(salt), //盐
""
);
return authenticationInfo;
}
}
这一部分就可以理解为,查看出数据库该角色所拥有的权限,也就是知道了那些界面能够访问
5.Shiro的配置类
5.1 Realm
@Bean
public Realm realm(){
Realm realm = new Realm();
//最简单的情况就是明文直接匹配,然后就是加密匹配,这里的匹配工作则就是交给
//CredentialsMatcher来完成的
//使用加密匹配
realm.setCredentialsMatcher(credentialsMatcher);
return realm;
}
@Bean注解重中之重,因为用的配置类
5.2 创建安全管理器DefaultWebSecurityManager
@Bean("securityManager")
public SecurityManager defaultWebSecurityManager(@Qualifier("managerRealm") ManagerRealm managerRealm){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
securityManager.setRealm(managerRealm);
/* List<Realm> realms=new ArrayList<>();
realms.add(managerRealm);
realms.add(userinfoRealm);
securityManager.setRealms(realms);*/
return securityManager;
}
5.3 创建ShiroFilterFactoryBean
@Bean("shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") SecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon:无需认证就可以访问
authc:必须认证才可以访问
user:必须拥有 记住我功能才能用
perms:拥有对某个资源的权限才能访问
role:拥有某个角色权限才能访问
*/
Map<String,String> filterMap=new LinkedHashMap<>();
/* filterMap.put("/manage/list","authc");*/
//这个意思是要去访问manage下面的路径时候,如果没有登录不能访问
filterMap.put("/manage/**","authc");
//无权限时候访问到登录页面
bean.setUnauthorizedUrl("/m/login");
//登录路径
bean.setLoginUrl("/m/login");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
5.5盐
说到了盐,是shiro的底层源码有的,可以帮助我们直接使用md5加密,注册时可以通过使用
SimpleHash simpleHash= new SimpleHash("md5","未加密的密码","盐",加密次数);
来进行给获得到的密码加密,这样加密的方法相同,或得到的密码值也相同
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(1);//加密的次数;
return hashedCredentialsMatcher;
}
5.4 贴上完整代码
@Configuration
public class ShiroConfig {
@Bean("shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") SecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon:无需认证就可以访问
authc:必须认证才可以访问
user:必须拥有 记住我功能才能用
perms:拥有对某个资源的权限才能访问
role:拥有某个角色权限才能访问
*/
Map<String,String> filterMap=new LinkedHashMap<>();
//这个意思是没有登录的访问/manage/任意的,都不行
filterMap.put("/manage/**","authc");
//无权限时候访问 去登录页面也就是
bean.setUnauthorizedUrl("/m/login");
filterMap.put("/manage/**","pers[sajdla]");
bean.setLoginUrl("/m/login");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
@Bean("securityManager")
public SecurityManager defaultWebSecurityManager(@Qualifier("managerRealm") ManagerRealm managerRealm){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
securityManager.setRealm(managerRealm);
/* List<Realm> realms=new ArrayList<>();
realms.add(managerRealm);
realms.add(userinfoRealm);
securityManager.setRealms(realms);*/
return securityManager;
}
@Bean
public ManagerRealm managerRealm(){
Realm realm = new Realm();
realm.setCredentialsMatcher(credentialsMatcher);
return realm;
}
/*@Bean
public UserinfoRealm userinfoRealm(){
return new UserinfoRealm();
}*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");//散列算法:这里使用MD5算法;
hashedCredentialsMatcher.setHashIterations(2);//加密的次数;
return hashedCredentialsMatcher;
}
//启用 controller 的权限注解
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(manager); return advisor; }
// 开启代理
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
}