本文档的代码中不会放出查询数据库的代码,以及数据库表结构的SQL,这些请去案例文件中寻找。如果看不懂请先看自定义Realm
注意:Shiro并不接管注册,需要程序猿实现
下列代码是我自定义的Realm,在无参构造中设置了如何加密,在登录验证方法的倒数第二行指定了如何加盐。
/**
* 自定义Realm,需要注入到Spring IoC中
*/
@Component
public class MyRealm extends AuthorizingRealm {
private final String salt = "black";//盐
public MyRealm() {
super.setName("MyRealm");//设置Realm的名字
//准备Shiro的加密类
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("MD5");//指定加密类型
matcher.setHashIterations(2);//指定加密次数
super.setCredentialsMatcher(matcher);
}
/**
* 完成登录认证的方法
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取账号信息
String username = (String) authenticationToken.getPrincipal();
//查询出该账号正确的密码
String password = usersMapper.getPasswordByUsername(username);
if (password == null)
throw new AuthenticationException("该账号不存在,验证失败");
// 验证密码,密码验证shiro已经实现,只用给shiro传入正确的密码就好
AuthenticationInfo info = new SimpleAuthenticationInfo(username, password, "MyRealm");//第三个参数传入的是Realm的类名
//加盐
((SimpleAuthenticationInfo) info).setCredentialsSalt(ByteSource.Util.bytes(salt));
return info;
}
}
下列代码是用户注册的Service,加密方式和加盐方式要和上列代码一样,否则登录认证是不会通过的。
@Service
public class UserService {
@Autowired
private UsersMapper usersMapper;
@Autowired
private UserRolesPermissionsMapper userRolesPermissionsMapper;
private final String memberRole = "member";//会员角色
private final String salt = "black";//盐
/**
* 用户注册,并默认分配角色为“member”
*
* @param user
* @return
*/
@Transactional(timeout = 5, rollbackFor = {ArithmeticException.class, Exception.class})
public void register(Users user) throws Exception {
if (usersMapper.getPasswordByUsername(user.getUsername()) != null) {
throw new Exception("该用户已存在[" + user.getUsername() + "]");
}
usersMapper.saveUser(user.getUsername(), this.encryption(user.getPassword()));
userRolesPermissionsMapper.setRole(user.getUsername(), memberRole);
}
/**
* 加密方法,要与Shiro登录验证的加密规则一样
* 这个方法一般只在注册时使用
*
* @param password 待加密的明文
* @return
*/
private String encryption(String password) {
String hashAlgorithmName = "MD5";//加密算法名称
int hashIterations = 2;//加密次数
ByteSource credentialsSalt = ByteSource.Util.bytes(salt);//加盐
return new SimpleHash(hashAlgorithmName, password, credentialsSalt, hashIterations).toString();
}
}
控制器:
@RestController
public class Test {
@Autowired
private MyRealm myRealm;
@Autowired
private UserService userService;
/**
* 登录验证接口
*/
@GetMapping(value = "/login", params = {"username", "password"})
public Object test(Users user) {
try {
//1:构建securityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(myRealm);
//2:主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//3:登录(Shiro接管了登录功能),如果传入的账号密码和simpleAccountRealm对象的不同则会账号或密码错误的异常
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
subject.login(token);
//3:检查对当用户是不是指定角色
subject.checkRoles("member");
//查看认证结果
return "认证结果:" + subject.isAuthenticated();
} catch (Exception e) {
return "认证失败,错误信息为:" + e;
}
}
/**
* 注册接口
*/
@GetMapping(value = "/register", params = {"username", "password"})
public Object register(Users user) {
try {
userService.register(user);//注册
return this.test(user);//注册成功就去登录
} catch (Exception e) {
return "认证失败,错误信息为:" + e;
}
}
}
调用注册接口:
再次调用注册接口,传入相同的账号,会提示你该账号已存在
调用登录接口
登录故意传错密码,会返回你密码错误