我的公众号:MarkerHub,Java网站:https://markerhub.com

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

小Hub领读:

导入jar包,配置yml参数,编写ShiroConfig定义DefaultWebSecurityManager,重写Realm,编写controller,编写页面,零打碎敲。搞定,是个高手~


下面一篇文章中,咱们曾经晓得了shiro的认证与受权过程,这也是shiro外面最外围罕用的根底性能。当初咱们把shiro集成到咱们的我的项目中,开始搭建一个有认证和权限体系的我的项目,比方用户核心须要登录之后能力拜访等!

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

集成Shiro

依据官网文档:
https://shiro.apache.org/spring-boot.html

第一步:集成导入jar包:

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

有些同学还在用shiro-spring的jar包,然而集成的配置就绝对多一点,所以能够间接应用starter包更加不便。

第二步:写好配置,官网给咱们提供的属性参数,以及一些默认值,如果不合乎咱们的需要,能够自行改变哈。

从配置上就能够看出,shiro的注解性能,rememberMe等性能已近主动集成进来了。所以starter包应用起来还是非常简单的,只须要相熟shiro的流程,从0开发不在话下哈。

  • application.yml
shiro:  web:    enabled: true  loginUrl: /loginspring:  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 过滤器链配置
@Configurationpublic 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
@Servicepublic 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
@Controllerpublic 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真不错!