一 相关依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
二 配置application.yml
server:
port: 8080
servlet:
session:
tracking-modes: cookie
spring:
datasource:
username: root
password: 6666
url: jdbc:mysql:///shiro?serverTimezone=Asia/Shanghai&characterEncoding=utf8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
thymeleaf:
encoding: UTF-8
servlet:
content-type: text/html
cache: false
prefix: classpath:/templates
suffix: .html
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
三 编写 shirorealm 来获取数据
package com.qf.realm;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class ShiroRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
}
四 编写shiroConfig 编写配置类 创建所需对象 和环境
package com.qf.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.qf.realm.ShiroRealm;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
@Configuration //相当于配置文件
public class ShiroConfig {
//realm域对象
public ShiroRealm getShiroRealm(){
return new ShiroRealm();
}
//安全管理对象 设置securitymanager环境
@Bean
public SecurityManager getSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(getShiroRealm());
return securityManager;
}
//Shiro过滤器配置 必须书写过滤器 将manager设置里
//注意配置过滤路径的顺序
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager){
//创建Shiro工厂对象
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//未认证访问页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
//认证成功跳转页面(一般不设置,默认登录成功跳转当前页面)
//shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权访问页面
shiroFilterFactoryBean.setUnauthorizedUrl("/refuse");
//通过map配置访问流程(顺序很重要)
LinkedHashMap<String, String> map = new LinkedHashMap<>();
//配置静态资源
map.put("/js/**","anon");
map.put("/css/**","anon");
map.put("/jquery/**","anon");
map.put("/layui/**","anon");
//配置login页面
map.put("/login","anon");
//配置logout退出
//map.put("/logout","logout");
//配置授权
//map.put("/delete","perms[user:delete]");
//map.put("/select","perms[user:select]");
//user设置记住我(二次登录不做认证操作)
//map.put("/index","user");
map.put("/**","authc");//该路径认证后才能访问
//map.put("/**","anon");//所有路径都可以匿名访问
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
}
过滤器简称 对应的java类 anon org.apache.shiro.web.filter.authc.AnonymousFilter authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter port org.apache.shiro.web.filter.authz.PortFilter rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter ssl org.apache.shiro.web.filter.authz.SslFilter user org.apache.shiro.web.filter.authc.UserFilter logout org.apache.shiro.web.filter.authc.LogoutFilter
顺序
anon:admins/**=anon 没有参数,表示可以匿名使用。
authc:/admins/user/**=authc表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数
roles:/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。
perms:/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。
rest:/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。
port:/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。
authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证
ssl:/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
user:/admins/user/**=user没有参数表示必须存在用户, 身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查
anon,authcBasic,authc,user是认证过滤器
perms,roles,ssl,rest,port是授权过滤器
五 创建controller
package com.qf.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserController {
@RequestMapping("toLogin")
public String toLgoin(){
System.out.println("toLogin");
return "/login";
}
@RequestMapping("login")
public String login(){
System.out.println("login");
return "/login";
}
@RequestMapping("refuse")
public String refuse(){
System.out.println("refuse");
return "/refuse";
}
@RequestMapping("index")
public String index(){
System.out.println("index");
return "/index";
}
@RequestMapping("add")
public String add(){
System.out.println("add");
return "/add";
}
@RequestMapping("select")
public String select(){
System.out.println("select");
return "/select";
}
@RequestMapping("delete")
public String delete(){
System.out.println("delete");
return "/delete";
}
// @RequestMapping("logout")
// public String logout(){
// System.out.println("logout");
// Subject subject = SecurityUtils.getSubject();
// subject.logout();
// return "/login";
// }
}
六 编写HTML
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <center> <form action="login"> 用户名:<input type="text" name="username"><br> <br> 密码:<input type="password" name="password"><br> <br> <input type="submit" value="登录"> </form> </center> </body> </html>
refuse.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1><font color="red">拒绝访问!!!</font> </h1> </body> </html>
index.html
<!DOCTYPE html> <html lang="en" xmlns:shiro="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>主页面</h1> <a href="add">添加用户</a> <a href="select">查询用户</a> <a href="delete">删除用户</a> <a href="update">修改用户</a> <a href="/logout">退出</a> </body> </html>
add.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>添加用户</h1> </body> </html>
select.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>查询用户</h1> </body> </html>
delete.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>删除用户</h1> </body> </html>
七实现用户认证,修改UserController中的login方法
@RequestMapping("login")
public String login(User user){
System.out.println("login");
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(User.getUserName(), User.getPassword);
subject.login(token);
boolean authenticated = subject.isAuthenticated();
if(authenticated){
return "/index";
}
return "/login";
}
八修改ShiroRealm中的AuthenticationInfo方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String usercode = (String) token.getPrincipal();
String password2 = new String((char[]) token.getCredentials());
String password = new Md5Hash(password2).toString();
com.entity.User user = userService.findUser(usercode, password);
if (user != null) {
if (user.getUsercode().equals(usercode) && user.getPassword().equals(password)) {
SimpleAuthenticationInfo simpleAuthenticationInfo =
new SimpleAuthenticationInfo(usercode, password2, "my");//必须是前端传过来的密码
return simpleAuthenticationInfo;
} else {
return null;
}
}
return null;
}
}
九实现用户授权,在ShiroConfig中的shiroFilter方法中添加被授权的信息
//配置授权
map.put("/delete","perms[user:delete]");
map.put("/select","perms[user:select]");
十修改ShiroRealm中的doGetAuthorizationInfo方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
HashSet<String> set = new HashSet<>();
set.add("user:select");
set.add("user:update");
simpleAuthorizationInfo.addStringPermissions(set);
return simpleAuthorizationInfo;
}
十一Shiro整合thymleaf标签
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
在config
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
3.修改index.HTML
<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>主页面</h1>
<a href="add">添加用户</a>
<a href="select">查询用户</a>
<a href="delete" shiro:hasPermission="user:delete">删除用户</a>
//shiro 如果没有权限 看不见
<a href="update" shiro:hasPermission="user:update">修改用户</a>
<a href="/logout">退出</a>
</body>
</html>
十二 退出
在index.HTML 加<a href="/logout">退出</a>
shiro过滤器 //配置退出
map.put("/logout","logout");
十三 rememMe
在login.HTML 页面
<input type="checkbox" name="rememberMe">记住我<br> <br>
UserController中的login中配置rememberMe
@RequestMapping("login")
public String login(String username, String password,boolean rememberMe){
System.out.println("login");
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password,rememberMe);
subject.login(token);
boolean authenticated = subject.isAuthenticated();
if(authenticated){
return "/index";
}
return "/login";
}
在shiroConfig类中添加相关的方法
package com.qf.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.qf.realm.ShiroRealm;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
@Configuration
public class ShiroConfig {
@Bean //3
public CookieRememberMeManager getCookieRememberMeManager(){
SimpleCookie simpleCookie = new SimpleCookie("renemberMe");
simpleCookie.setMaxAge(3600*24*31);
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(simpleCookie);
return cookieRememberMeManager;
}
public ShiroRealm getShiroRealm(){
return new ShiroRealm();
}
@Bean
public SecurityManager getSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(getShiroRealm());
securityManager.setRememberMeManager(getCookieRememberMeManager());//3
return securityManager;
}
//配置ShiroDialect 用来没有权限不显示页面
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
//shiro过滤器配置
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//未认证访问页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
//认证成功跳转页面(一般不设置,默认登录成功跳转当前页面)
//shiroFilterFactoryBean.setSuccessUrl("/index");
//未授权访问页面
shiroFilterFactoryBean.setUnauthorizedUrl("/refuse");
//通过map配置访问流程(顺序很重要)
LinkedHashMap<String, String> map = new LinkedHashMap<>();
//配置静态资源
map.put("/js/**","anon");
map.put("/css/**","anon");
map.put("/jquery/**","anon");
map.put("/layui/**","anon");
//配置login页面
map.put("/login","anon");
//配置logout退出
map.put("/logout","logout");
//配置授权
map.put("/delete","perms[user:delete]"); //2
map.put("/select","perms[user:select]"); //2
//user设置记住我(二次登录不做认证操作)
map.put("/index","user");
map.put("/**","authc");//该路径认证后才能访问
//map.put("/**","anon");//所有路径都可以匿名访问
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
}
十四 连接数据库
授权AuthorizationInfo
通过SimpleAuthorizationInfo 进行授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String usercode = (String) principals.getPrimaryPrincipal();
Set<String> permissions = new HashSet<>();
SimpleAuthorizationInfo authorizationInfo =
new SimpleAuthorizationInfo();
List<User> allPermissions = userService.findAllPermissions(usercode);
for (int i = 0; i < allPermissions.size(); i++) {
permissions.add(allPermissions.get(i).getPercode());
}
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
SQL 语句
SELECT su.usercode,sr.name,sp.percode
from sys_user su
INNER JOIN sys_role sr
ON su.id=sr.available
INNER JOIN sys_role_permission srp
on sr.id=srp.sys_role_id
INNER JOIN sys_permission sp
ON srp.sys_permission_id =sp.id
where su.usercode='lisi'
多表内连接
内连接,外连接实际上都是在笛卡尔积(join)的基础上对记录进行筛选。
等值连接和非等值连接:这两者同时包含在内连接和外连接中,因为内连接和外连接都是需要连接条件的,条件为=则为等值连接,反之为非等值连接。
自然连接:等值连接的一种,使用natural join后面可以不使用on接查询条件,默认会将关联表中的相同字段进行比较,查询出的结果相同的字段会去重(值必须相等)。
内连接:使用inner join和join连接都行,重点是要有查询条件,条件使用on或者where引导查询都行,查询出的结果为两表都匹配的记录。
十五报错
1.找不到secumaanger 因为ShiroFilterFactoryBean没配置securitymanager