认证思路
-
调用
SecurityUtils.getSubject()
方法,获取当前的Subject
对象 ; -
调用
Subject.isAuthenticated()
测试当前的用户,即Subject
是否以及被认证,即是否登录 ; -
如果没有被认证,则进行认证操作;
-
将用户名和密码封装成
UsernamePasswordToken
对象; -
自定义
Realm
的方法,从数据库获取对应的记录,返回给shiro
;如果是仅仅完成登录,则继承
AuthenticatingRealm
类,实现doGetAuthenticationInfo(AuthenticationToken)
方法 ; -
调用
Subject.login(AuthenticationToken)
方法,执行登录 ; -
由
shiro
完成密码的比对 。
自定义用于登录检验的Realm的思路
在认证的时候,
Subject.login(AuthenticationToken)
方法,传进去的参数,最终被传到了AuthenticatingRealm
的自定义子类中,用于认证的方法中;
-
将方法中
token
参数,强转为UsernamePasswordToken
对象; -
从
UsernamePasswordToken
中获取 用户名 ; -
调用数据库方法,获取此用户名对用的记录;
-
如若此用户不存在,则抛出
UnknownAccountException
异常; -
用户存在的情况下,根据实际情况看是否需要抛出其他
AuthenticationException
异常,比如:用户被锁定异常
; -
根据用户情况,构建
AuthenticationInfo
对象返回,最常用的是实现类是:SimpleAuthenticationInfo
;如果不需要进行盐值加密,则调用有三个参数的构造器;
principal
: 认证的实体信息,也就上面我们是使用什么信息进行认证,比如这里就是使用 用户名 ;credentials
:数据库中保存的密码;realmName
:当前realm
的名字,调用父类的getName()
方法即可 ;
代码实现
控制器代码:
@RequestMapping(value = "/isAllowLogin", method = {RequestMethod.POST})
@ResponseBody
public String isAllowLogin(@RequestParam("username") String username, @RequestParam("password") String password) throws IOException {
JSONObject jsonObject = new JSONObject();
Subject subject = SecurityUtils.getSubject();
if (!subject.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
subject.login(token);
} catch (AuthenticationException e) {
jsonObject.put("result", "0");
return jsonObject.toJSONString();
}
}
jsonObject.put("result", "1");
return jsonObject.toJSONString();
}
自定义Realm
代码:
@Service
public class ShiroRealm extends AuthenticatingRealm {
@Autowired
private UserMapper userMapper;
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
User user = userMapper.findUserByName(username);
if (null == user) {
throw new UnknownAccountException("用户名或密码错误");
}
String password = user.getUserPass();
AuthenticationInfo info = new SimpleAuthenticationInfo(username, password, getName());
return info;
}
}
后记
shiro
的校验通过以后,同一个 session
对应的浏览器再次输入 任意登录信息
都会校验通过,这是 shiro
拿缓存了,没有走 realm
校验 ;
要想登出,可以使用在受保护页面那里配置一个 logout
进行登出操作;
<property name="filterChainDefinitions">
<value>
/logout.action = logout
...
....
...
</value>
</property>
这里注意,前面的路径随便写,无论其存在不存在,只要后面写成 logout
就行, shiro
就会执行登出操作;其原理应该是销毁了对应的 session
;