博客项目学习笔记十二:登录注册功能(登录)
博客项目目录: 请戳这里
准备
需求:实现用户登录功能,登录之后,跳转到首页,并且页面信息由游客状态变为用户状态
1.引入shiro依赖包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>net.mingsoft</groupId>
<artifactId>shiro-freemarker-tags</artifactId>
<version>0.1</version>
</dependency>
2.设计controller层中的登录动作
主要包括三步:
- 验证邮箱和密码是否为空
- 将邮箱和密码封装为token
- 验证token以及相应的异常处理
@ResponseBody
@PostMapping("/login")
public Result dologin(String email, String password){
//邮箱和密码为空验证
if(StrUtil.isEmpty(email)||StrUtil.isBlank(password)){
Result.fail("邮箱或密码不能为空");
}
//获取token
UsernamePasswordToken token = new UsernamePasswordToken(email, SecureUtil.md5(password));
//验证token及异常处理
try {
SecurityUtils.getSubject().login(token);
} catch (AuthenticationException e) {
if (e instanceof UnknownAccountException) {
return Result.fail("用户不存在");
} else if (e instanceof LockedAccountException) {
return Result.fail("用户被禁用");
} else if (e instanceof IncorrectCredentialsException) {
return Result.fail("密码错误");
} else {
return Result.fail("用户认证失败");
}
}
return Result.success().action("/");
}
通过debug发现token最后交给了一个Realm进行验证,所以我们需要自定义一个Realm

3.自定义Realm以及Profile类
新建shiro包,自定义AccountRealm类和AccountProfile类
由于用户数据经常会用到,所以我们将相关信息封装为一个Profile类
@Data
public class AccountProfile implements Serializable {
private Long id;
private String username;
private String email;
private String sign;
private String avatar;
private String gender;
private Date created;
public String getSex() {
return "0".equals(gender) ? "女" : "男";
}
}
doGetAuthenticationInfo 是我们认证的方法,authenticationToken 是我们的传过来的UsernamePasswordToken ,包含邮箱和密码。然后 userService.login 的内容就是校验一下账户的合法性,不合法就抛出对应的异常,合法最终就返回封装对象 AccountProfile。
@Component
public class AccountRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
AccountProfile profile = userService.login(usernamePasswordToken.getUsername(), String.valueOf( usernamePasswordToken.getPassword()));
SecurityUtils.getSubject().getSession().setAttribute("profile", profile);
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(profile, token.getCredentials(), getName());
return info;
}
}
4.配置Realm和过滤器
@Slf4j
@Configuration
public class ShiroConfig {
@Bean
public SecurityManager securityManager(AccountRealm accountRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(accountRealm);
log.info("------------------>securityManager注入成功");
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager);
// 配置登录的url和登录成功的url
filterFactoryBean.setLoginUrl("/login");
filterFactoryBean.setSuccessUrl("/user/center");
// 配置未授权跳转页面
filterFactoryBean.setUnauthorizedUrl("/error/403");
Map<String, String> map = new LinkedHashMap<>();
map.put("/login","anon");
filterFactoryBean.setFilterChainDefinitionMap(map);
return filterFactoryBean;
}
}
5.登录接口
public interface UserService extends IService<User> {
AccountProfile login(String email, String password);
}
6.在impl层实现登录
- 首先通过query查询拿到user
- 然后验证用户是否拿到以及密码是否正确
- 接着跟新登录日期,用户信息
- 最后通过反射拿到个人账户信息
User实体类自动生成的时候日期使用的是LocalDateTime,需要改为Date类型。
@Override
public AccountProfile login(String email, String password) {
//拿到用户
User user = this.getOne(new QueryWrapper<User>().eq("email", email));
//用户为空或密码不正确的处理方式
if(user == null) {
throw new UnknownAccountException();
}
if(!user.getPassword().equals(password)){
throw new IncorrectCredentialsException();
}
//跟新最后登录日期,然后跟新用户信息
user.setLasted(new Date());
this.updateById(user);
//通过反射拿到个人账户信息
AccountProfile profile = new AccountProfile();
BeanUtil.copyProperties(user, profile);
return profile;
}
7.调试代码,进行测试
在相应位置打断点,浏览器进入,点击登录

8.在配置类注入shiro标签

9.在header.ftl引入相关标签

<!-- 未登入的状态 -->
<@shiro.guest>
<li class="layui-nav-item">
<a class="iconfont icon-touxiang layui-hide-xs" href="user/login.html"></a>
</li>
<li class="layui-nav-item">
<a href="/login">登入</a>
</li>
<li class="layui-nav-item">
<a href="/register">注册</a>
</li>
<li class="layui-nav-item layui-hide-xs">
<a href="/app/qq/" onclick="layer.msg('正在通过QQ登入', {icon:16, shade: 0.1, time:0})" title="QQ登入" class="iconfont icon-qq"></a>
</li>
<li class="layui-nav-item layui-hide-xs">
<a href="/app/weibo/" onclick="layer.msg('正在通过微博登入', {icon:16, shade: 0.1, time:0})" title="微博登入" class="iconfont icon-weibo"></a>
</li>
</@shiro.guest>
<!-- 登入后的状态 -->
<@shiro.user>
<li class="layui-nav-item">
<a class="fly-nav-avatar" href="javascript:;">
<cite class="layui-hide-xs">贤心</cite>
<i class="iconfont icon-renzheng layui-hide-xs" title="认证信息:layui 作者"></i>
<i class="layui-badge fly-badge-vip layui-hide-xs">VIP3</i>
<img src="https://tva1.sinaimg.cn/crop.0.0.118.118.180/5db11ff4gw1e77d3nqrv8j203b03cweg.jpg">
</a>
<dl class="layui-nav-child">
<dd><a href="user/set.html"><i class="layui-icon"></i>基本设置</a></dd>
<dd><a href="user/message.html"><i class="iconfont icon-tongzhi" style="top: 4px;"></i>我的消息</a></dd>
<dd><a href="user/home.html"><i class="layui-icon" style="margin-left: 2px; font-size: 22px;"></i>我的主页</a></dd>
<hr style="margin: 5px 0;">
<dd><a href="/user/logout/" style="text-align: center;">退出</a></dd>
</dl>
</li>
</@shiro.user>
10.运行项目,进行测试
发现登陆后,右上角有用户名字和头像了

11.填充实际的用户信息
- 修改header.ftl
![在这里插入图片描述]()
- 测试
![在这里插入图片描述]()
12.实现退出登录功能
- 执行log.out方法,并重定向到首页
@RequestMapping("/user/logout") public String logout(){ SecurityUtils.getSubject().logout(); return "redirect:/"; } - 测试内容:
登录之后,点击退出
![在这里插入图片描述]()
- 测试结果:
发现又重新回到首页
![在这里插入图片描述]()
参考资料:
https://github.com/MarkerHub/eblog
https://blog.csdn.net/sayoko06/article/details/80897658





浙公网安备 33010602011771号