1.shiro配置
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.8.0</version>
</dependency>
<!--shiro redis缓存权限-->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis-spring-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
2.使用步骤(基本配置步骤)
shiroConfig文件中设置域
@Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager dws = new DefaultWebSecurityManager(); // 设置域 (认证、授权 需要调用dao类 这个类中完成的登陆、查询、curd) dws.setRealm(myRealm()); return dws; }在设置域的同时创建myRealm配置文件,继承AuthorizingRealm类
public class MysqlRealm extends AuthorizingRealm { @Autowired SysUserMapper sysUserMapper; /** * 授权方法 当需要验证当前用户是否具有角色或授权时, 会自动调用 **/ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection p) { return null; } /** * 认证方法 当用户登录时,会自动调用 * * 登录流程: * 1. 用户输入账号密码 保存在usernamePasswordTken对象中 * 2. 登录时会自动调用认证方法, 拿到登录的用户名去数据库查询 * 3. 如果用户名不存在 抛出相应异常,表示登录失败 * 4. 如果数据库存在该用户 就进行密码对比, 对比失败 抛出密码不对的异常 * 5. 如果没有任何异常抛出 说明比对成功, 登录成功 * */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken a) throws AuthenticationException { // 拿到登录时 输入的用户名 String username = (String) a.getPrincipal(); // 去数据库查询该用户是否存在 SysUser user = sysUserMapper.findUser(username); // 如果账号不存在 抛出异常 if(user == null) { throw new UnknownAccountException(); } // 判断账号是否被锁定 密码不需要自己抛异常 if(user.getSuo()==1) { throw new LockedAccountException(); } // 设置得到数据库的盐 ByteSource bytes = ByteSource.Util.bytes(user.getSalt()); // 将数据库的账号密码对象 返回出去 和登录输入的账号密码进行对比 SimpleAuthenticationInfo sai = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),bytes,getName()); return sai; } }在Controller中进行简单使用
@Autowired SysUserMapper sysUserMapper; @RequestMapping("login") public String login(String yhm, String pwd, RedirectAttributes ra, HttpSession session,String jzw){ // 拿到当前操作对象 Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(yhm,pwd); try { if(jzw!=null){ // 勾选记住我 token.setRememberMe(true); } subject.login(token); //登录成功后保存业务到session SysUser user = sysUserMapper.findUser(yhm); session.setAttribute("users",user); return "index"; } catch (UnknownAccountException uae){ ra.addFlashAttribute("msg","用户名不存在"); } catch (IncorrectCredentialsException ice){ ra.addFlashAttribute("msg","密码错误"); } catch (LockedAccountException lae){ ra.addFlashAttribute("msg","用户已被锁定"); } // 登录失败,重定向到登录页 return "redirect:/"; }
3.shiro最终配置
1.ShiroConfig,主要配置
- ShiroDialect :thymleaf中使用shiro标签库
- DefaultWebSecurityManager:设置域,设置会话管理器、记住我管理器
- Realm:加密设置,缓存配置(重点,提交登录时账号密码比对效率)
- ShiroFilterFactoryBean: shiro过滤器(重点)。
- CookieRememberMeManager:自定义cookie参数
package com.zking.config;
@Configuration
public class ShiroConfig {
// thymleaf标签中使用shiro标签库
@Bean
public ShiroDialect sd() {
return new ShiroDialect();
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager dws = new DefaultWebSecurityManager();
// 设置会话管理器
dws.setSessionManager(new DefaultWebSessionManager());
// 设置记住我管理器
dws.setRememberMeManager(ck());
// 设置域 (认证、授权 需要调用dao类 这个类中完成的登陆、查询、curd)
dws.setRealm(myRealm());
return dws;
}
@Bean
public CookieRememberMeManager ck() {
CookieRememberMeManager ck = new CookieRememberMeManager();
SimpleCookie sc = new SimpleCookie();
sc.setHttpOnly(true); //防止js读取cookie
sc.setMaxAge(40); // 设置cookir时长
sc.setName("zhg"); // cookie名字
ck.setCookie(sc);
return ck;
}
@Bean
public Realm myRealm() {
MysqlRealm realm = new MysqlRealm();
// 设置密码加密,登陆时进行加密对比
HashedCredentialsMatcher hcm = new HashedCredentialsMatcher();
hcm.setHashAlgorithmName("sha-256");
hcm.setHashIterations(100);
realm.setCredentialsMatcher(hcm);
/*// 配置redis缓存
RedisCacheManager cm = new RedisCacheManager();
realm.setCacheManager(cm);*/
return realm;
}
// shiro过滤器对象
@Bean("shiroFilterFactoryBean") // 在spring容器中的id 必须是这个
public ShiroFilterFactoryBean filterFactoryBean() {
ShiroFilterFactoryBean sff = new ShiroFilterFactoryBean();
// 设置安全管理器对象
sff.setSecurityManager(securityManager());
///基本设置///
// 检测到没有登录的跳转地址
sff.setLoginUrl("/login.html");
// 检测到没有权限的跳转地址
sff.setUnauthorizedUrl("/qx.html");
// 自定义过滤器,没有先后顺序
Map<String , Filter> myFilter = new HashMap();
myFilter.put("myroles",new MyRolesFilter());
// 注销过滤器
LogoutFilter logout = new LogoutFilter();
logout.setRedirectUrl("/index.html");
myFilter.put("logout",logout);
sff.setFilters(myFilter);
// 控制器过滤设置,有先后顺序 //
/*
* anon 允许匿名访问,不需要登录
* user 需要登录后才能进行访问
* roles 需要具有指定的所有角色
* perms 需要具有指定的所有权限
* logout 注销
* authc 一定要输入账号密码,不包含记住我,安全性更高
* */
Map<String ,String > map = new LinkedHashMap<>();
map.put("/*.html","anon");
map.put("/zhuxiao","logout"); // 注销登录 LogoutFilter类 底层原理
/* map.put("/songs/find","anon");
map.put("/songs/delete","user,roles[经理,组长]");
map.put("/songs/update","user");
map.put("/songs/insert","authc");*/
// map.put("songs/","");
sff.setFilterChainDefinitionMap(map);
return sff;
}
}
2.MyRealm中主要配置
- 继承AuthorizingRealm
- 实现授权方法:doGetAuthorizationInfo
- 实现登录方法:doGetAuthenticationInfo
package com.zking.config;
public class MysqlRealm extends AuthorizingRealm {
@Autowired
SysUserMapper sysUserMapper;
/**
* 授权方法 当需要验证当前用户是否具有角色或授权时, 会自动调用
**/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection p) {
// 得到当前登录的用户名
String yhm = (String) p.getPrimaryPrincipal();
// 查询该用户的角色与权限
List<String> roles = sysUserMapper.findRoles(yhm);
List<String> perms = sysUserMapper.findPerms(yhm);
System.out.println(yhm+"具有角色"+roles);
System.out.println(yhm+"具有权限"+perms);
// 授权并返回
SimpleAuthorizationInfo s= new SimpleAuthorizationInfo();
s.addRoles(roles);
s.addStringPermissions(perms);
return s;
}
/**
* 认证方法 当用户登录时,会自动调用
*
* 登录流程:
* 1. 用户输入账号密码 保存在usernamePasswordTken对象中
* 2. 登录时会自动调用认证方法, 拿到登录的用户名去数据库查询
* 3. 如果用户名不存在 抛出相应异常,表示登录失败
* 4. 如果数据库存在该用户 就进行密码对比, 对比失败 抛出密码不对的异常
* 5. 如果没有任何异常抛出 说明比对成功, 登录成功
* */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken a) throws AuthenticationException {
// 拿到登录时 输入的用户名
String username = (String) a.getPrincipal();
// 去数据库查询该用户是否存在
SysUser user = sysUserMapper.findUser(username);
// 如果账号不存在 抛出异常
if(user == null) {
throw new UnknownAccountException();
}
// 判断账号是否被锁定 密码不需要自己抛异常
if(user.getSuo()==1) {
throw new LockedAccountException();
}
// 设置得到数据库的盐
ByteSource bytes = ByteSource.Util.bytes(user.getSalt());
// 将数据库的账号密码对象 返回出去 和登录输入的账号密码进行对比
SimpleAuthenticationInfo sai = new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),bytes,getName());
return sai;
}
}