前言:
由于博客的接口有部分只能由管理员来调用。但如果每个接口都亲自写代码进行权限管理就过于麻烦了,所以引入了shiro这个安全框架,顺带也将原来不规范的密码明文存储在数据库,改为了md5加密。
博客的地址:blog.awakeyoyoyo.com
shiro基本知识
这里略过了,上一篇文章基本带大家入了个门了。
step1
增加4个表来进行角色权限的管理
角色表:
权限表:
用户-角色中间表
角色-权限中间表
由于仅仅需要用于权限管理,表的设计比较简陋。
step2
修改pom.xml文件新增shiro依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
step3
新增springboot shiro配置类
主要配置三个shiro重要的bean
1.Realm(用于认证以及授权)
2.DefaultWebSecurityManager(用于管理realm)
3.shiroFilterFactoryBean (用于配置shiro过滤器依赖与安全管理器)
他们之间的关系是由下向上依赖。所以利用spring的注解@Qualifier将其一一对应起来即可。
shiro过滤器的配置规矩
由于其内部实现是用 LinkedHashMap实现,所以我们跟着新建就好
Map<String, String> filterMap = new LinkedHashMap();
anon:无需认真即可访问
authc:必须认证了才可以访问
user:必须拥有记住我功能才能访问
perms:拥有对某个资源的权限才可以访问
role:拥有某个角色权限才可以访问
键值对的形式来进行配置。看下方代码基本可以看懂,我都一一进行了注释。
本来配置了个shiro的密码加密器,但由于本博客的内部结构有些特殊,所以选择自己重写了一个realm来完善。
@Configuration
public class ShiroConfig {
//shiroFilterFactoryBean:3
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
//设置安全管理器
bean.setUnauthorizedUrl("/toerror");
bean.setLoginUrl("/tologin");
bean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon:无需认真即可访问
authc:必须认证了才可以访问
user:必须拥有记住我功能才能访问
perms:拥有对某个资源的权限才可以访问
role:拥有某个角色权限才可以访问
*/
Map<String, String> filterMap = new LinkedHashMap();
filterMap.put("/writeBlog","roles[admin]");
filterMap.put("/editBlog","roles[admin]");
filterMap.put("/publishBlog","roles[admin]");
//授权
filterMap.put("/**","anon");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
//DefaultWebSecurityManager:2
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
// @Bean("hashedCredentialsMatcher")
// public HashedCredentialsMatcher hashedCredentialsMatcher() {
// HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
// hashedCredentialsMatcher.setHashAlgorithmName("md5");// 散列算法:这里使用MD5算法;
// hashedCredentialsMatcher.setHashIterations(2);// 散列的次数,比如散列两次,相当于md5(md5(""));
// //hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);// 表示是否存储散列后的密码为16进制,需要和生成密码时的一样,默认是base64;
// return hashedCredentialsMatcher;
// }
@Bean
public UserRealm userRealm(){
UserRealm userRealm=new UserRealm();
// userRealm.setCredentialsMatcher(matcher);
// userRealm.setAuthorizationCachingEnabled(true);
return userRealm;
}
}
step4
自定义realm
这个挺简单的,基本注释都有,自己看吧。
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleMapper roleMapper;
@Autowired
private PermissionMapper permissionMapper;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了doGetAuthorizationInfo-----授权");
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
User user=(User) SecurityUtils.getSubject().getPrincipal();
Set<String> roles=new HashSet<String>();
List<String> rolesByUserId=roleMapper.getRolesByUserId(user.getId());
for(String role:rolesByUserId) {
roles.add(role);
}
List<String> permissionsByUserName = permissionMapper.getPermissionsByUserId(user.getId());
for(String permission:permissionsByUserName) {
info.addStringPermission(permission);
}
info.setRoles(roles);
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了doGetAuthenticationInfo 认真");
UsernamePasswordToken userToken=(UsernamePasswordToken)authenticationToken;
User user=userMapper.selectUserByaccoun_id(userToken.getUsername());
if (user==null){
return null;
}
String password=String.valueOf(userToken.getPassword());
if (user.getPassword().equals(password)){
//通过token验证
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user,user.getPassword(),getName());
return info;
}
//账号密码验证
String salt = user.getSalt();
// System.out.println(userToken.getUsername()+"===="+userToken.getPassword().toString());
String encodedPassword = ShiroMd5Util.shiroEncryption(password,salt);
// System.out.println("密码:"+encodedPassword);
userToken.setPassword(encodedPassword.toCharArray());
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user,user.getPassword(),getName());
// info.setCredentialsSalt(ByteSource.Util.bytes(user.getSalt()));
return info;
}
}
总结:
shiro是一个很好用的安全框架,本博客网站用到的只是他的一些基本功能,主要用来对于博客的一些接口进行权限管理,shiro还实现了一个不依赖web容器的session(与绑定subject,而subject又存储与线程上下文),让我们的开发更加方便。