环境搭建
首先我是基于Springboot整合shiro框架进行的。然后用Mybatis进行的数据库连接操作;
准备工作
喜闻乐见的导包:
<dependencies>
<!--Springboot组件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--数据库操作-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!--日志-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--lombok组件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--shiro
Subject 用户
SecurityManager 权限管理
Reolam 连接数据
-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!--导入模板引擎-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
设置配置文件
application.yml
##数据库连接
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/mydb?serverTimezone=UTC&useUnicode=true&charaching=utf-8
type: com.alibaba.druid.pool.DruidDataSource
dbcp2:
initial-size: 5
min-idle: 5
max-wait-millis: 600000
max-total: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 30000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
##数据库扫描
mybatis:
type-aliases-package: com.awei.springboot_shiro.pojo
mapper-locations: classpath:mybatis/*.xml
数据库
数据库名: mydb
然后创建一张表叫做: user
字段就简单的设置一些:
给些数据
连接数据库
这是项目目录,很简单得一个Demo;
连接数据库就不多赘述了
mapper接口
@Mapper
@Repository
public interface usermapper {
User querUserByUsername(String username);
}
mapper.xml
<mapper namespace="com.awei.springboot_shiro.mapper.usermapper">
<select id="querUserByUsername" parameterType="String" resultType="user">
select * from user where username = #{username}
</select>
</mapper>
userservice
public interface userservice {
public User querUserByUsername(String username);
}
userServiceImpl
@Service
public class userServiceImpl implements userservice {
@Autowired
usermapper usermapper;
@Override
public User querUserByUsername(String username) {
User user = usermapper.querUserByUsername(username);
return user;
}
}
User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer userId;
private String username;
private String password;
private String perms;
}
前端界面(非常丑,测试用)
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>Title</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}" style="color: red"></p>
<div th:if="${session.loginUser}==null">
<a th:href="@{/tologin}">登录</a>
</div>
<div th:if="${session.loginUser}!=null">
<a th:href="@{/logout}">退出登录</a><br>
</div>
<div shiro:hasPermission = "user:add">
<a th:href="@{user/add}">add</a>
</div>
<div shiro:hasPermission = "user:update">
<a th:href="@{/user/update}">update</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>Title</title>
</head>
<body>
<form th:action="@{/login}" method="post">
<P>用户名:<input type="text" name="username"></P>
<P>密码:<input type="text" name="password"></P>
<p><input type="submit"></p>
</form>
</body>
</html>
user文件夹下得add.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
this is add html
</body>
</html>
update.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
this is update html
</body>
</html>
关键点来了~~
首先是导入了shiro依赖后关键是
config
Realm
shiroConfig:
@Configuration
public class shiroconfig {
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(defaultWebSecurityManager);
/**
* anon :无需认证直接访问
* authc:必须认证了才能访问
* user:必须remmeber me 才能访问
* perms:必须拥有某个权限才能访问
* role:必须拥有某个角色才能访问
*/
//拦截
HashMap<String,String> map = new HashMap<>();
//权限操作
map.put("/user/add","perms[user:add]");
map.put("/logout","logout");
map.put("/user/update","perms[user:update]");
map.put("/user/*","authc");
bean.setFilterChainDefinitionMap(map);
//设置跳转登录界面
bean.setLoginUrl("/tologin");
//设置未授权请求
bean.setUnauthorizedUrl("/noauth");
return bean;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
@Bean
public UserRealm userRealm(){
return userRealm;
}
//整合模板引擎
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
从下往上看就容易一些,模板引擎只是在这里申明一下要用,从userRealm()开始看
这里边就把是否登录了? 还有登录这个人有啥子角色权限?这些信息存住,然后丢给
上边得DefaultWebSecurityManager()方法让他去执行判断,放行还是拦住。然后
这个Manager就去到了ShiroFilterFactoryBean()拦截器这边,这里卡死所有访问
路径,然后让Manager来看是不是要放行或者拦住;通过存住得数据和拦截规则进行匹配
然后上边需要一个Realm;从哪儿来,写呗
UserRealm:
public class UserRealm extends AuthorizingRealm{
@Autowired
userservice userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//需要拿到用户信息并且将权限赋予用户
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();
String perms = currentUser.getPerms();
//查询出来用户具有多个权限赋予
Set<String> set=new HashSet<>();
String[] split = perms.split(",");
Arrays.stream(split).forEach(perm ->set.add(perm));
info.setStringPermissions(set);
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken Token) throws AuthenticationException {
//连接数据库
UsernamePasswordToken token = (UsernamePasswordToken)Token;
User user = userService.querUserByUsername(token.getUsername());
if(user == null){
return null;
}
//Subject 获取用户信息
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
//放入session然后控制前端显示对应按钮
session.setAttribute("loginUser",user);
//自动获取密码
//MD5 加模 MD5盐值加密 MD5不可逆
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
return info;
}
}
好的这里就是上边得config需要得realm了 这个又重要了,首先看到
继承至AuthorizingRealm重写得两个长得贼像得方法
doGetAuthorizationInfo():授权
doGetAuthenticationInfo():认证
认证说白了就是登录告诉他 他帮你判断是啥子问题
例如:没有用户,账号错误,密码错误。。。
认证完了,说明有这么个人,但是授权就是通过你这个人去数据库
找到他有啥子角色啊 权限啊,弄出来告诉这个realm。他就完全了
就可以进行config里边得判断操作了。
一饮一琢,前因后果;
最后还有一个
@Controller
public class mycontroller {
@RequestMapping({"/","/index"})
public String toindex(Model model){
model.addAttribute("msg","shiro is success");
return "index";
}
@RequestMapping("/user/add")
public String toadd(){
return "/user/add";
}
@RequestMapping("/user/update")
public String toupdate(){
return "/user/update";
}
@RequestMapping("/tologin")
public String tologin(){
return "/login";
}
@RequestMapping("/login")
public String login(String username , String password , Model model){
//获取当前用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//判定登录方法
try {
subject.login(token);
model.addAttribute("msg","登录成功");
return "/index";
} catch (UnknownAccountException e) {
model.addAttribute("msg","用户名错误");
return "/index";
}catch (IncorrectCredentialsException e) {
model.addAttribute("msg","密码错误");
return "/index";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
return "未经授权,无法访问";
}
}
这里就只有这个登录看着有点绕,总而言之subject,把登录得参数弄给他就好了;
他能带着这些东西到处跑。
总而言之,就是简单,直接照搬,数据库也弄一样得,随意操作。其实密码需要加密处理,不能明文操作得,无所谓了。小白篇,自娱自乐用,不要当真~