共计 6652 个字符,预计需要花费 17 分钟才能阅读完成。
我的公众号: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: /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 是 AuthenticatingRealm
和AuthorizingRealm
。阐明源码外面曾经通过了一些封装,所以咱们就不能再间接继承 Realm
,那么AuthenticatingRealm
和AuthorizingRealm
咱们继承哪个呢?咱们发现 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 真不错!