只须要6个步骤,springboot集成shiro,并完成登陆

个人公众号: MarkerHub,Java网站: https://markerhub.com

更多精选文章请点击:Java笔记大全.mdhtml

小Hub领读:

导入jar包,配置yml参数,编写ShiroConfig定义DefaultWebSecurityManager,重写Realm,编写controller,编写页面,一鼓作气。搞定,是个高手~前端


上面一篇文章中,咱们已经知道了shiro的认证与受权过程,这也是shiro里面最核心经常使用的基础功能。如今咱们把shiro集成到咱们的项目中,开始搭建一个有认证和权限体系的项目,好比用户中心须要登陆以后才能访问等!java

一、极简入门,Shiro的认证与受权流程解析git

集成Shiro

根据官方文档:
https://shiro.apache.org/spring-boot.htmlgithub

第一步:集成导入jar包:web

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring-boot-web-starter</artifactId>
    <version>1.4.2</version>
</dependency>

有些同窗还在用shiro-spring的jar包,可是集成的配置就相对多一点,因此能够直接使用starter包更加方便。redis

第二步:写好配置,官方给咱们提供的属性参数,以及一些默认值,若是不符合咱们的需求,能够自行改动哈。spring

从配置上就能够看出,shiro的注解功能,rememberMe等功能已近自动集成进来了。因此starter包使用起来仍是很是简单的,只须要熟悉shiro的流程,从0开发不在话下哈。apache

  • application.yml
shiro:
  web:
    enabled: true
  loginUrl: /login
spring:
  freemarker:
    suffix: .ftl # 注意新版本后缀是 .ftlh
    template-loader-path: classpath:/templates/
    settings:
      classic_compatible: true #处理空值

上面的配置,我就改了一下登陆的url,其余都是使用默认的,做为咱们最简单的测试,相信大家。缓存

第三步:配置shiro的securityManager和自定义realm。由于realm负责咱们的认证与受权,因此是必须的,自定义的realm必需要交给securityManager管理,因此这两个类须要重写。而后还有一些资源的权限说明,因此通常须要定义ShiroFilterChainDefinition,因此有3个类咱们常写的:

  • AuthorizingRealm
  • DefaultWebSecurityManager shiro的核心管理器
  • ShiroFilterChainDefinition 过滤器链配置
@Configuration
public class ShiroConfig {

    @Bean
    AccountRealm accountRealm() {
        return new AccountRealm();
    }

    @Bean
    public DefaultWebSecurityManager securityManager(AccountRealm accountRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(accountRealm);
        return securityManager;
    }

    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        // logged in users with the 'admin' role
        chainDefinition.addPathDefinition("/admin/**", "authc, roles[admin]");

        // logged in users with the 'document:read' permission
        chainDefinition.addPathDefinition("/docs/**", "authc, perms[document:read]");

        chainDefinition.addPathDefinition("/login", "anon");
        chainDefinition.addPathDefinition("/doLogin", "anon");

        // all other paths require a logged in user
        chainDefinition.addPathDefinition("/**", "authc");
        return chainDefinition;
    }
}

上面说到ShiroFilterChainDefinition是定义过滤器配置的,啥意思呢,咱们来看看其中一句:

chainDefinition.addPathDefinition("/admin/**", "authc, roles[admin]");

这一句代码意思是说:访问/admin/**开头的连接,都须要已经完成登陆认证authc、而且拥有admin角色权限才能访问。

你能够看到key-value是连接-过滤器的组合,过滤器能够同时多个。那么authc、role、perms、anon究竟是哪来的呢?有啥特殊意义?是啥拦截器?

咱们来看下这个说明文档:

能够看到,其实每一个简写单词,都是一个过滤器的名称。好比authc表明这FormAuthenticationFilter。每一个过滤器具体是啥用的?咱们看几个经常使用的吧:

  • authc 基于表单的拦截器,没有登陆会跳到相应的登陆页面登陆
  • user 用户拦截器,用户已经身份验证 / 记住我登陆的均可
  • anon 匿名拦截器,即不须要登陆便可访问
  • roles 角色受权拦截器,验证用户是否拥有全部角色
  • perms 权限受权拦截器,验证用户是否拥有全部权限

第四步:第ok,根据需求项目的资源制定项目过滤器链ShiroFilterChainDefinition。咱们再回到AccountRealm这个类。咱们以前说过,认证受权的过程,咱们是在Realm里面完成的。因此咱们须要继承Realm,并实现两个方法。

可是这里须要注意,咱们通常不直接继承Realm,能够看看Realm接口:

  • org.apache.shiro.realm.Realm
public interface Realm {
    String getName();

    boolean supports(AuthenticationToken token);

    AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException;

}

而从上一篇文章中,咱们分析的认证受权的源码过程时候,你会看到,认证和受权分别调用的realm是AuthenticatingRealmAuthorizingRealm。说明源码里面已经通过了一些封装,因此咱们就不能再直接继承Realm,那么AuthenticatingRealmAuthorizingRealm咱们继承哪一个呢?咱们发现AuthorizingRealm是继承AuthenticatingRealm的,因此在重写realm的时候,咱们只须要集成超类AuthorizingRealm便可。

public abstract class AuthorizingRealm extends AuthenticatingRealm

因此,结合了受权与验证,还有缓存功能,咱们自定义Realm的时候继承AuthorizingRealm便可。

  • com.markerhub.shiro.AccountRealm
public class AccountRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    /**
     * 受权方法
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        AccountProfile principal = (AccountProfile) principalCollection.getPrimaryPrincipal();

        // 硬编码(赋予用户权限或角色)
        if(principal.getUsername().equals("MarkerHub")){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addRole("admin");
            return info;
        }

        return null;
    }

    /**
     * 认证方法
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        AccountProfile profile = userService.login(token.getUsername(), String.valueOf(token.getPassword()));
        // 把用户信息存到session中,方便前端展现
        SecurityUtils.getSubject().getSession().setAttribute("profile", profile);

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(profile, token.getCredentials(), getName());
        return info;
    }
}
  • com.markerhub.service.impl.UserServiceImpl
@Service
public class UserServiceImpl implements UserService {

    @Override
    public AccountProfile login(String username, String password) {

        //TODO 查库,而后匹配密码是否正确!

        if(!"MarkerHub".equals(username)) {
            // 抛出shiro异常,方便通知用户登陆错误信息
            throw new UnknownAccountException("用户不存在");
        }
        if(!"111111".equals(password)) {
            throw new IncorrectCredentialsException("密码错误");
        }

        AccountProfile profile = new AccountProfile();
        profile.setId(1L);
        profile.setUsername("MarkerHub");
        profile.setSign("欢迎关注公众号MarkerHub哈");

        return profile;
    }
}

上面代码中,我login方法直接给出了帐号MarkerHub,并赋予了角色admin。

第五步:ok,准备动做已经热身完毕,接下来咱们去编写登陆、退出接口,以及咱们的界面:

  • com.markerhub.controller.IndexController
@Controller
public class IndexController {

    @Autowired
    HttpServletRequest req;

    @RequestMapping({"/", "/index"})
    public String index() {
        System.out.println("已登陆,正在访问!!");
        return "index";
    }

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    /**
     * 登陆
     */
    @PostMapping("/doLogin")
    public String doLogin(String username, String password) {

        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            SecurityUtils.getSubject().login(token);

        } catch (AuthenticationException e) {
            if (e instanceof UnknownAccountException) {
                req.setAttribute("errorMess", "用户不存在");
            } else if (e instanceof LockedAccountException) {
                req.setAttribute("errorMess", "用户被禁用");
            } else if (e instanceof IncorrectCredentialsException) {
                req.setAttribute("errorMess", "密码错误");
            } else {
                req.setAttribute("errorMess", "用户认证失败");
            }
            return "/login";
        }
        return "redirect:/";
    }


    /**
     * 退出登陆
     */
    @GetMapping("/logout")
    public String logout() {
        SecurityUtils.getSubject().logout();
        return "redirect:/login";
    }

}

第六步:登陆页面:

  • templates/login.ftl
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8"/>
    <title>MarkerHub 登陆</title>
</head>
<body>
    <h1>用户登陆</h1>
    <h3>欢迎关注公众号:MarkerHub</h3>

    <form method="post" action="/doLogin">
        username: <input name="username" type="text">
        password: <input name="password" type="password">
        <input type="submit" name="提交">
    </form>

    <div style="color: red;">${errorMess}</div>
</body>
</html>

登陆成功页面:

  • templates/index.ftl
<h1>登陆成功:${profile.username}</h1>
<h3>${profile.sign}</h3>

<div><a href="/logout">退出</a></div>

ok,代码咱们已经编写完成,接下来,咱们运行项目,而后访问首页,将自行跳转到登陆页面,而后输入帐号密码以后,咱们能够看到完成登陆!

登陆界面:

登陆成功页面:

结束语

好了,今天作了一个极简的登陆注册功能,介绍了一下shiro的基本整合步骤。流程仍是挺简单的哈哈,不知道你看懂了没。

而在一些负载均衡的场景中,咱们的会话信息是须要共享的,因此shiro通常会和redis整合在一块儿,你知道怎么整合吗?咱们明天再聊哈,记得来哦,MarkerHub天天发文时间19点20分。

附:demo git源码地址https://github.com/MarkerHub/...


(完)

推荐阅读

Java笔记大全.md

太赞了,这个Java网站,什么项目都有!https://markerhub.com

这个B站的UP主,讲的java真不错!

相关文章
相关标签/搜索