博客项目学习笔记十二:登录注册功能(登录)

博客项目目录: 请戳这里

准备

需求:实现用户登录功能,登录之后,跳转到首页,并且页面信息由游客状态变为用户状态

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">&#xe620;</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;">&#xe68e;</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

posted @ 2021-05-24 20:53  xqxls  阅读(432)  评论(0)    收藏  举报