情景
我的项目中有六个用户角色(学校管理员,学生等),需要进行分别登陆。如果在一个realm中,对controller封装好的Token进行Service验证,需要在此realm中注入六个数据库操作对象,然后写一堆if语句来判断应该使用那个Service服务,然后再在验证方法(doGetAuthorizationInfo)中写一堆if来进行分别授权,这样写不仅会让代码可读性会非常低而且很难后期维护修改(刚写完的时候只有上帝和你能看懂你写的是什么,一个月之后你写的是什么就只有上帝能看懂了)。
所以一定要配置多个realm来分别进行认证授权操作。shiro有对多个realm的处理,当配置了多个Realm时,shiro会用自带的org.apache.shiro.authc.pam.ModularRealmAuthenticator类的doAuthenticate方法来进行realm判断,源码:
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws Authenticatio<span style="color:transparent">来1源gaodai#ma#com搞*代#码1网</span>nException { assertRealmsConfigured(); Collection<Realm> realms = getRealms(); if (realms.size() == 1) { return doSingleRealmAuthentication(realms.iterator().next(), authenticationToken); } else { return doMultiRealmAuthentication(realms, authenticationToken); } }
assertRealmsConfigured();的作用是验证realm列表是否为空,如果一个realm也没有则会抛出IllegalStateException异常(爆红:Configuration error: No realms have been configured! One or more realms must be present to execute an authentication attempt.)
当realm只有一个时直接返回,当realm有多个时返回所有的realm。而我们要做的就是写多个realm后重写ModularRealmAuthenticator下的doAuthenticate方法,使它能满足我们的项目需求。
那么改怎么重写ModularRealmAuthenticator下的doAuthenticate方法,使它能满足我们的项目需求呢?这就需要分析我们使用shiro的使用方法了。
shiro的使用
1.Controller层中,获取当前用户后将用户名和密码封装UsernamePasswordToken对象,然后调用Subject中的登陆方法subject.login(UsernamePasswordToken)
@RequestMapping("/user/login") @ResponseBody public String Login(String userName,String password){ //获取当前用户 subject Subject subject = SecurityUtils.getSubject(); //封装用户的登陆数据 UsernamePasswordToken token = new UsernamePasswordToken(userName, password); try{ subject.login(token);//执行登陆方法 return "登陆成功"; }catch (UnknownAccountException e){//用户名不存在 model.addAttribute("msg","用户名不存在"); return "用户名不存在"; }catch (IncorrectCredentialsException e){//密码错误 model.addAttribute("msg","密码错误"); return "密码错误"; } }
(为了测试方便,我用了@ResponseBody返回字符串)
2.完善自定义Realm类,继承于AuthorizingRealm,主要实现doGetAuthorizationInfo和doGetAuthenticationInfo方法
(需要实现认证和授权方法,在这里方便测试主要是认证)
public class StudentRealm extends AuthorizingRealm { @Resource private StudentsService studentsService; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { return null; } //认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("Shiro=========Student认证"); UserToken userToken = (UserToken) token; Students students = studentsService.queryByNum(userToken.getUsername()); //账号不存在 if (students == null) { System.out.println("学生不存在"); //向上层提交UnknownAccountException异常,在controller层处理 throw new UnknownAccountException(); } //密码认证,shiro来做,可以自定义加密方式 return new SimpleAuthenticationInfo("", students.getPassword(), USER_LOGIN_TYPE); } }