Apache shiro学习文档
作者:pinkhub
时间:2021/12/24
技术栈:springboot+mybatisPlus+shiro
未来,我们抱团取暖,结伴而行…
第一章 Apache Shiro 概述
Apache Shiro 是一个强大而灵活的开源安全框架,可以干净地处理身份验证、授权、企业会话管理和加密。
身份验证:有时也被称为“登录”,这是一种证明用户真实身份的行为。
授权:访问控制的过程,即确定“谁”可以访问“什么”。
会话管理:管理特定于用户的会话,即使是在非 Web 或 EJB 应用程序中。
密码学:使用密码算法确保数据安全,同时仍然易于使用。
官方文档:https://shiro.apache.org/reference.html
第二章 Apache Shiro 架构
关键字:Subject 、SecurityManager``和``Realms
第三章 Apache Shiro 配置
3.1 配置文件(ini)配置
基于INI配置使DefinitionRealm、definitionRealm生效
# =======================
# Shiro INI configuration
# =======================
[main]
# Objects and their properties are defined here,
# Such as the securityManager, Realms and anything
# else needed to build the SecurityManager
definitionRealm=edu.pinkhub.shrio_demo.realm.DefinitionRealm
securityManager.realms=$definitionRealm
[users]
# The 'users' section is for simple deployments
# when you only need a small number of statically-defined
# set of User accounts.
jay=1234
[roles]
# The 'roles' section is for simple deployments
# when you only need a small number of statically-defined
# roles.
[urls]
# The 'urls' section is used for url-based security
# in web applications. We'll discuss this section in the
# Web documentation
3.2 配置类配置
@Configuration
public class ShiroConfig {
@Bean("shiroRealm")
public ShiroRealm shiroRealm(){
return new ShiroRealm();
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm);
return securityManager;
}
}
第四章 Apache Shiro 核心
前提
导入shiro依赖:可以选择shiro-spring-boot-starter 、shiro-spring 、shiro-core 的一个。
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
tips:
如果选择shiro-core依赖时,版本不要太高,否则IniSecurityManagerFactory 方法过时
4.1 编码和解码算法
shiro提供base64和16进制字符串编码和解码的API支持。
工具类如下:
public class EncodesUtil {
public static String encodeHex(byte[] input){
return Hex.encodeToString(input);
}
public static byte[] decodeHex(String input){
return Hex.decode(input);
}
public static String encodeBase64(byte[] input){
return Base64.encodeToString(input);
}
public static byte[] decodeBase64(String input){
return Base64.decode(input);
}
}
4.2 散列算法
散列算法用于生成数据的摘要信息,不可逆算法,常用于存储密码,常见的散列算法有:MD5、SHA等,散列的对象:“密码+salt”,salt其实是干扰数据。
散列算法的5种实现类: tips:其中6种加密实现类继承于SimpleHash 类
关键字:salt→SecureRandomNumberGenerator
? password→SimpleHash
摘要算法工具类:用于对密码加密
public class DigestsUtil {
public static final String SHA1="SHA-1";
public static final Integer ITERATION=512;
public static String sha1(String plaintext,String salt){
return new SimpleHash(SHA1,plaintext,salt,ITERATION).toString();
}
public static String createSalt(){
SecureRandomNumberGenerator secureRandomNumberGenerator = new SecureRandomNumberGenerator();
return secureRandomNumberGenerator.nextBytes().toHex();
}
public static Map<String,String> entryptPassword(String pwd){
HashMap<String, String> map = new HashMap<>();
String salt=createSalt();
String password=sha1(pwd,salt);
map.put("salt",salt);
map.put("password",password);
return map;
}
}
测试类:
System.out.println(DigestsUtil.entryptPassword("123").toString());
运行结果:
{password=54eeefc1368c375feeac0f71e1e6c929d4d5d6f1, salt=ba18ca250fd8442eaccfa3a03c3a5530}
4.3 认证
认证流程
【第一步】:Shiro把用户的数据封装成标识token,token一般封装着用户名,密码等信息 UsernamePasswordToken
【第二步】:使用Subject门面获取到封装着用户的数据的标识token subject.login(usernamePasswordToken);
【第三步】:Subject把标识token交给SecurityManager,SecurityManager再把标识token委托给认证器Authenticator进行身份验证。
? 认证器的作用一般是用来指定如何验证,它规定本次认证用到哪些Realm
【第四步】:认证器Authenticator将传入的标识token,与数据源Realm对比,验证token是否合法
关键字:
doGetAuthenticationInfo 、SimpleAuthenticationInfo
案例一:登陆测试1
数据来自shiro.ini文件
第一步:导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
第二步:创建shiro.ini
#声明用户账号
[users]
jay=1234
第三步:测试代码
public void shiroLogin(){
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jay", "1234");
subject.login(usernamePasswordToken);
System.out.println("登陆状态:"+subject.isAuthenticated());
}
截图:
案例二:登陆测试2
数据来自数据库
【第一步】:自定义realm
public class DefinitionRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String loginName = (String)authenticationToken.getPrincipal();
SecurityServiceImpl securityService = new SecurityServiceImpl();
String password = securityService.findPasswordByUserName(loginName);
if(password==""){
throw new AuthenticationException("账号不存在");
}
return new SimpleAuthenticationInfo(loginName,password,getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
【第二步】:创建shiro.ini definitionRealm生效
[main]
definitionRealm=edu.pinkhub.shrio_demo.realm.DefinitionRealm
securityManager.realms=$definitionRealm
【第三步】:测试
@Test
public void shiroLogin(){
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("jay", "1234");
subject.login(usernamePasswordToken);
System.out.println("登陆状态:"+subject.isAuthenticated());
}
案例三:登录测试3
Realm使用散列算法模拟登录
【第一步】:创建shiro.ini,使自定义realm生效
[main]
definitionRealm=edu.pinkhub.shrio_demo.realm.DefinitionRealm
securityManager.realms=$definitionRealm
【第二步】:创建service接口
public interface SecurityService {
Map<String,String> findPasswordByUserName(String userName);
}
【第三步】:创建service实现类,模拟根据用户名从数据库查询其加密密码、角色列表、权限列表
@Service
public class SecurityServiceImpl implements SecurityService {
@Override
public Map<String,String> findPasswordByUserName(String userName) {
return DigestsUtil.entryptPassword("123456");
}
}
【第四步】:创建realm
public class DefinitionRealm extends AuthorizingRealm {
public DefinitionRealm(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);
hashedCredentialsMatcher.setHashIterations(DigestsUtil.ITERATION);
setCredentialsMatcher(hashedCredentialsMatcher);
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String loginName = (String)authenticationToken.getPrincipal();
SecurityServiceImpl securityService = new SecurityServiceImpl();
Map<String, String> map = securityService.findPasswordByUserName(loginName);
if(map.isEmpty()){
throw new AuthenticationException("账号不存在");
}
String password=map.get("password");
String salt = map.get("salt");
return new SimpleAuthenticationInfo(loginName,password, ByteSource.Util.bytes(salt),getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
【第五步】:编写测试类
@Test
public void shiroLogin(){
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("pinkhub", "123456");
subject.login(usernamePasswordToken);
System.out.println("登陆状态:"+subject.isAuthenticated());
}
源码追踪:
将token、info密码匹配结果boolean值,返回subject.isAuthenticated()
4.4 授权
前提:用户必须通过认证;角色和权限存放在数据库中
基本流程
【第一步】:首先调用Subject.isPermitted*/hasRole* 接口,然后委托给SecurityManager (安全管理器)
【第二步】:SecurityManager 在委托给内部组件Authorizer (授权器)
【第三步】:Authorizer 再将请求委托给Realm 去做
【第四步】:Realm 将用户请求的参数封装成权限对象,再从我们重写的doGetAuthorizationInfo 方法中获取从数据库中查询到的权限集合
【第五步】:Realm 将用户传入的权限对象,与从数据库查出的权限对象进行一一对比。如果用户传入的权限对象在数据库中查出来的权限对象中,则返回true,否则返回false
关键字:doGetAuthorizationInfo 、SimpleAuthorizationInfo
ShiroConfig配置
配置内容:
(1)创建自定义ShiroDbRealm 实现,用于权限认证、授权、加密方式的管理,同时从数据库中取得相关的角色、资源、用户的信息,然后交于DefaultWebSecurityManager权限管理器管理
(2)创建DefaultWebSecurityManager 权限管理器用于管理DefaultWebSessionManager会话管理器、ShiroDbRealm
(3)创建ShiroFilterFactoryBean 的shiro过滤器指定权限管理器、同时启动连接链及登录URL、未登录的URL的跳转
(4)创建SimpleCookie ,访问项目时,会在客户端中cookie中存放ShiroSession
(5)创建DefaultWebSessionManager 会话管理器定义cookie机制、定时刷新、全局会话超时时间然后交于DefaultWebSecurityManager权限管理器管理
@Configuration
public class ShiroConfig {
@Bean("shiroDBRealm")
public ShiroDBRealm shiroDBRealm() {
return new ShiroDBRealm();
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("shiroDBRealm") ShiroDBRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
HashMap<String, String> filterMap = new HashMap<>();
filterMap.put("/user/add", "perms[add]");
filterMap.put("/user/edit", "perms[update]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
shiroFilterFactoryBean.setLoginUrl("/user/toLogin");
shiroFilterFactoryBean.setUnauthorizedUrl("/user/noAuth");
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
}
案例一:用户权限校验
登陆状态:true
当前用户是否拥有管理员角色true
当前用户是否拥有查看订单的权限true
当前用户没有coder角色
【1】模拟从数据库查出的角色和权限列表
@Override
public Map<String,String> findPasswordByUserName(String userName) {
return DigestsUtil.entryptPassword("123456");
}
@Override
public List<String> findRoleByUserName(String userName) {
List<String> roleList = new ArrayList();
roleList.add("admin");
roleList.add("dev");
roleList.add("student");
return roleList;
}
@Override
public List<String> findPermissionByUserName(String UserName) {
List<String> list=new ArrayList();
list.add("order:add");
list.add("order:del");
list.add("order:update");
list.add("order:query");
return list;
}
【2】编写doGetAuthorizationInfo
public class DefinitionRealm extends AuthorizingRealm {
public DefinitionRealm(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);
hashedCredentialsMatcher.setHashIterations(DigestsUtil.ITERATION);
setCredentialsMatcher(hashedCredentialsMatcher);
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String loginName = (String)authenticationToken.getPrincipal();
System.out.println(loginName);
SecurityServiceImpl securityService = new SecurityServiceImpl();
Map<String, String> map = securityService.findPasswordByUserName(loginName);
if(map.isEmpty()){
throw new AuthenticationException("账号不存在");
}
System.out.println(getName());
String password=map.get("password");
String salt = map.get("salt");
return new SimpleAuthenticationInfo(loginName,password, ByteSource.Util.bytes(salt),getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
String primaryPrincipal = (String)principal.getPrimaryPrincipal();
SecurityServiceImpl securityService = new SecurityServiceImpl();
List<String> roles = securityService.findRoleByUserName(primaryPrincipal);
List<String> permissions = securityService.findPermissionByUserName(primaryPrincipal);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.addRoles(roles);
authorizationInfo.addStringPermissions(permissions);
return authorizationInfo;
}
}
【3】编写测试代码
@Test
public Subject login(){
IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
System.out.println(subject);
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("pinkhub", "123456");
subject.login(usernamePasswordToken);
return subject;
}
@Test
public void testRole(){
Subject subject = login();
System.out.println("登陆状态:"+subject.isAuthenticated());
System.out.println("当前用户是否拥有管理员角色"+subject.hasRole("admin"));
System.out.println("当前用户是否拥有查看订单的权限"+subject.isPermitted("order:query"));
try{
subject.checkRole("coder");
}catch(Exception e){
System.out.println("当前用户没有coder角色");
}
}
【4】运行截图
第五章 项目实战
登录、注册、用户权限管理
前提:准备user表;导入shiro依赖;导入DigestsUtil.java 、 Result.java 、 ResultCode.java 工具类
框架:sprintboot+mybatisPlus+shiro+thymeleaf
项目结构图: 【第0步】准备工作
(1)添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.1.0</version>
</dependency>
(2)编写配置文件yml
server:
port: 8847
spring:
datasource:
druid:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/books
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
【第一步】编写User实体类
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@TableName("tb_user")
public class User implements Serializable {
@TableId(value = "id",type = IdType.AUTO)
public Integer id;
@TableField("account")
public String account;
@TableField("real_name")
public String realName;
@TableField("password")
public String password;
@TableField("salt")
public String salt;
@TableField("title")
public String title;
@TableField("status")
public Integer status;
@TableField("user_id")
public String userId;
@TableField("perms")
public String perms;
}
【第二步】编写mapper层
@Mapper
public interface UserMapper extends BaseMapper<User> {}
【第三步】编写service层
---------------------------------------UserService.java-------------------------------------------------------------------------
public interface UserService extends IService<User> {}
---------------------------------------UserServiceImpl.java---------------------------------------------------------------------
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {}
【第四步】编写controller
Application.java
@Controller
public class AppplicationController {
@RequestMapping({"/","/index"})
public String index(Model model){
model.addAttribute("msg","hello,shiro");
return "login";
}
}
UserController.java
@RequestMapping("/user/")
@Controller
public class UserControlller {
@Autowired
public UserService userService;
@RequestMapping("toRegister")
public String toRegister(){
return "register";
}
@PostMapping("register")
public String register(HttpServletRequest request, Model model){
String account = request.getParameter("account");
String _password = request.getParameter("password");
Map<String, String> info = DigestsUtil.entryptPassword(_password);
String password = info.get("password");
String salt = info.get("salt");
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("account",account);
User userDB = userService.getOne(wrapper);
if(userDB!=null){
model.addAttribute("msg","账号已存在");
return "register";
}else{
User user = new User();
user.setAccount(account);
user.setPassword(password);
user.setSalt(salt);
user.setRealName("pinkhub");
user.setStatus(1);
Random random = new Random();
user.setUserId(Integer.toString(random.nextInt(1000)));
user.setTitle("普通用户");
user.setPerms("query,update");
userService.save(user);
return "login";
}
}
@PostMapping("login")
public String login(String account,String password, Model model){
UsernamePasswordToken token = new UsernamePasswordToken(account, password);
Subject subject = SecurityUtils.getSubject();
try{
subject.login(token);
return "index";
}catch(UnknownAccountException e){
model.addAttribute("msg","账号不正确");
return "login";
}catch(IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "login";
}
}
@RequestMapping("add")
public String add(){
return "/user/add";
}
@RequestMapping("edit")
public String edit(){
return "/user/edit";
}
@RequestMapping("noAuth")
public String noAuth(){
return "noAuth";
}
@RequestMapping("toLogin")
public String toLogin(){
return "login";
}
【第五步】编写realm
ShiroDBRealm.java
public class ShiroDBRealm extends AuthorizingRealm {
@Autowired
public UserService userService;
public ShiroDBRealm(){
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(DigestsUtil.SHA1);
matcher.setHashIterations(DigestsUtil.ITERATION);
setCredentialsMatcher(matcher);
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了doGetAuthenticationInfo()方法");
UsernamePasswordToken tk=(UsernamePasswordToken) token;
QueryWrapper<User> wrapper = new QueryWrapper<User>();
wrapper.eq("account",tk.getUsername());
User user = userService.getOne(wrapper);
if(user==null){
return null;
}
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.setAttribute("loginUser",user);
return new SimpleAuthenticationInfo(user,user.getPassword(), ByteSource.Util.bytes(user.getSalt()),getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
System.out.println("执行了doGetAuthorizationInfo()方法");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Subject subject = SecurityUtils.getSubject();
User user = (User)subject.getPrincipal();
System.out.println(user);
List<String> permList = new ArrayList<>();
String[] perms = user.getPerms().split(",");
for(String perm:perms){
permList.add(perm);
}
info.addStringPermissions(permList);
return info;
}
}
【第六步】编写config
ShiroConfig.java
@Configuration
public class ShiroConfig {
@Bean("shiroDBRealm")
public ShiroDBRealm shiroDBRealm() {
return new ShiroDBRealm();
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("shiroDBRealm") ShiroDBRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
HashMap<String, String> filterMap = new HashMap<>();
filterMap.put("/user/add", "perms[add]");
filterMap.put("/user/edit", "perms[update]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
shiroFilterFactoryBean.setLoginUrl("/user/toLogin");
shiroFilterFactoryBean.setUnauthorizedUrl("/user/noAuth");
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
【第七步】编写前端页面
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:shiro="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>shiro</title>
<style>
#header{
height: 75px;
}
</style>
</head>
<body>
<div id="header">
<img th:src="@{../apache-shiro-logo.png}" style="height: 100%">
</div>
<h1>首页</h1>
<p th:text="${msg}" style="color: red"></p>
<hr>
<div th:if="${session.loginUser==null}">
<a th:href="@{/user/toLogin}">登录</a>
</div>
<div shiro:hasPermission="add">
<a th:href="@{/user/add}" style="text-decoration: none">添加用户</a>
<span>|</span>
</div>
<div shiro:hasPermission="update">
<a th:href="@{/user/edit}" style="text-decoration: none">修改用户</a>
</div>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>shiro</title>
<style>
#login_form{
display: block;
position: absolute;
transform: translate(-50%, -61.8%);
left: 50%;
top: 50%;
width: 450px;
}
#header{
height: 75px;
}
</style>
</head>
<body>
<div id="header">
<img th:src="@{../apache-shiro-logo.png}" style="height: 100%">
</div>
<div id="login">
<h1>登陆页面</h1>
<div id="tips">
<p th:text="${msg}" style="color: red"></p>
</div>
<hr>
<div id="login_form">
<form action="/user/login" method="post">
账号:<input type="text" name="account"/><br><br>
密码:<input type="password" name="password"/><br><br>
<a th:href="@{/user/toRegister}" style="text-decoration: none">无账号,去注册</a><br><br>
<input type="submit" value="提交"/>
</form>
</div>
</div>
</body>
</html>
noAuth.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>shiro</title>
<style>
#header{
height: 75px;
}
</style>
</head>
<body>
<div id="header">
<img th:src="@{../apache-shiro-logo.png}" style="height: 100%">
</div>
<p>该用户无权限</p>
</body>
</html>
register.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>shiro</title>
<style>
#register_form{
display: block;
position: absolute;
transform: translate(-50%, -61.8%);
left: 50%;
top: 50%;
width: 450px;
}
#header{
height: 75px;
}
</style>
</head>
<body>
<div id="header">
<img th:src="@{../apache-shiro-logo.png}" style="height: 100%">
</div>
<div id="register">
<h1>注册页面</h1>
<div id="tips">
<p th:text="${msg}" style="color: red"></p>
</div>
<hr>
<div id="register_form">
<form th:action="@{/user/register}" method="post">
账号:<input type="text" name="account"/><br><br>
密码:<input type="password" name="password"/><br><br>
<input type="submit"/>
</form>
</div>
</div>
</body>
</html>
add.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>shiro</title>
<style>
#header{
height: 75px;
}
</style>
</head>
<body>
<div id="header">
<img th:src="@{../apache-shiro-logo.png}" style="height: 100%">
</div>
<h1>shiro权限控制</h1>
<a th:href="@{/user/toLogin}" style="text-decoration: none">退出</a>
<hr>
<p style="color: green">
增加用户
</p>
</body>
</html>
edit.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>shiro</title>
<style>
#header{
height: 75px;
}
</style>
</head>
<body>
<div id="header">
<img th:src="@{../apache-shiro-logo.png}" style="height: 100%">
</div>
<h1>shiro权限控制</h1>
<a th:href="@{/user/toLogin}" style="text-decoration: none">退出</a>
<hr>
<p style="color: green">
修改用户
</p>
</body>
</html>
项目截图
账号:00
密码:000000 权限:update
账号:qiang
密码:000000 权限:add
ending.....
|