shiro安全框架使用整合thymeleaf以及SpringBoot
导入相关依赖
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
编写Shiro配置类
首先是Realm
//权限操作
//自定义的Realm
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println ("执行了=》授权doGetAuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo ();
//info.addStringPermission ("user:add");
//拿到当前登录的对象
Subject subject = SecurityUtils.getSubject ();
User currentUser = (User) subject.getPrincipal (); // 拿到user对象
info.addStringPermission (currentUser.getPerms ());
return info;
}
//认证
@Override // AuthenticationToken token 来自于MyController中的 UsernamePasswordToken token = new UsernamePasswordToken (username,password);
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println ("执行了=》认证doGetAuthenticationInfo");
//用户名,密码 数据库中取
UsernamePasswordToken userToken= (UsernamePasswordToken) token;
User user = userService.queryUserByName (userToken.getUsername ());
if(user==null) //用户名出错
{
return null;
}
String password=user.getPwd ();
//密码可以加密:MD5 e10adc3949ba59abbe56e057f20f883e
// MD5盐值加密 e10adc3949ba59abbe56e057f20f883e+username/
//密码认证:shiro来封装
return new SimpleAuthenticationInfo (user,password,""); //传入user对象给授权,传入密码给shiro判断
}
}
ShiroConfig
//请求过滤
@Configuration
public class ShiroConfig {
//第一步 创建realm对象,需要自定义
@Bean(name = "userRealm")
public UserRealm userRealm(){
return new UserRealm ();
}
//第二步 DefaultWebSecurityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager ();
//关联Realm
securityManager.setRealm (userRealm);
return securityManager;
}
//第三步 ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager)
{
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean ();
//设置安全管理器
bean.setSecurityManager (defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
* anon:无需认证就可以访问
* authc:必须认证才可以访问
* user:必须拥有记住我(remember me)才能用
* perms:拥有对某个资源的权限才能访问
* role:拥有某个角色全选才能访问
* */
//拦截
Map<String,String> FilterChainDefinitionMap=new LinkedHashMap<> ();
/*FilterChainDefinitionMap.put ("/user/add","authc");
FilterChainDefinitionMap.put ("/user/update","authc");*/
//授权 正常情况下,没有授权会跳转到未授权页面
FilterChainDefinitionMap.put ("/user/add","perms[user:add]");
FilterChainDefinitionMap.put ("/user/update","perms[user:update]");
//设置未授权页面
bean.setUnauthorizedUrl ("/noauth");
bean.setFilterChainDefinitionMap (FilterChainDefinitionMap);
//设置登录请求
bean.setLoginUrl ("/toLogin");
return bean;
}
//整合shiroDialect 用来整合shiro-theymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect ();
}
}
Controller层
/*
* Subject 用户
* SecurityManager 管理用户
* Realm 连接数据
* */
/*导入整合spring的包*/
@Controller
public class MyController {
@RequestMapping({"/","index"})
public String toIndex(Model model){
model.addAttribute ("msg","hello shiro");
return "index";
}
@RequestMapping("/user/add")
public String addUser()
{
return "user/addUser";
}
@RequestMapping("/user/update")
public String updateUser()
{
return "user/updateUser";
}
@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); //执行登陆方法,没有异常就ok
Subject currentSubject = SecurityUtils.getSubject ();
Session session = currentSubject.getSession ();
session.setAttribute ("loginUser",username);
return "/index";
} catch (UnknownAccountException e) { //用户名不存在
model.addAttribute ("msg","用户名错误");
return "login";
} catch (IncorrectCredentialsException ice) { //异常:密码不对
model.addAttribute ("msg","密码错误");
return "login";
} catch (LockedAccountException lae) { //异常:用户名被锁定
model.addAttribute ("msg","用户名被锁定");
return "login";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized()
{
return "未经授权无法访问";
}
}
数据库层不做过多解释就先放一个mapper文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ji.mapper.UserMapper">
<select id="queryUserByName" parameterType="String" resultType="com.ji.pojo.User">
select * from `user`
where name=#{name}
</select>
</mapper>
数据库配置
spring:
datasource:
username: root
password: root
#假如时区报错,就增加一个时区的配置就ok了,serverTimezone=UTC
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#SpringBoot默认是不注入这些的,需要自己绑定
#druid数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许报错,java.lang.ClassNotFoundException: org.apache.Log4j.Properity
#则导入log4j 依赖就行
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis:
mapper-locations: classpath:mapper/*.xml
html文件
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<div th:if="${session.loginUser==null}">
<a th:href="@{/toLogin}">登录</a>
</div>
<p th:text="${msg}"></p>
<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.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/Login}">
<p>用户名<input type="text" name="username"></p>
<p>密码<input type="password" name="password"></p>
<p><input type="submit"></p>
</form>
</body>
</html>
shiro可进行密码加密主要继承此接口
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
public interface CredentialsMatcher {
/**
* Returns {@code true} if the provided token credentials match the stored account credentials,
* {@code false} otherwise.
*
* @param token the {@code AuthenticationToken} submitted during the authentication attempt
* @param info the {@code AuthenticationInfo} stored in the system.
* @return {@code true} if the provided token credentials match the stored account credentials,
* {@code false} otherwise.
*/
boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info);
}
我们可以自定义密码加密方式也可以使用如下加密方法