config配置
- UserRealm
package com.example.config;
import com.example.pojo.User;
import com.example.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
// 自定义的UserRealm
public class UserRealm extends AuthorizingRealm{
@Autowired
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();
// 设置当前用户的权限
info.addStringPermission(currentUser.getPerms());
return info;
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证doGetAuthorizationInfo");
// String name = "root";
// String password = "123";
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
User user = userService.queryUserByName(usernamePasswordToken.getUsername());
if(user == null){
return null; // 抛出异常 UnknownAccountException
}
// 密码认证,shiro做
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
}
- ShiroConfig
package com.example.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
// ShiroFilterFactoryBean 3
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
/*
添加shiro的内置过滤器
anno: 无需认证就可以访问
authc: 必须认证了才可以访问
user: 必须拥有记住我功能才可以访问
perms: 拥有对某个资源的权限才能访问
role: 拥有某个角色权限才能访问
*/
Map<String,String> filterMap = new LinkedHashMap<>();
// filterMap.put("/user/add","authc");
// filterMap.put("/user/update","authc");
// 拥有了user:add权限才可以访问/user/add接口
filterMap.put("/user/add","perms[user:add]");
// 认证了就可以访问user下的所有接口
filterMap.put("/user/*","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
// 设置登录请求
shiroFilterFactoryBean.setLoginUrl("/toLogin");
// 设置未授权的请求
shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");
return shiroFilterFactoryBean;
}
// DefaultWebSecurityManger 2
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
// 创建 realm对象 需要自定义类 1
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
// shiroDialect: 用来整合shiro和thymeleaf
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
controller层
import com.example.utils.MailUtil;
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.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class Test {
@RequestMapping({"/","/index"})
public String test(Model model){
model.addAttribute("msg","shiro测试");
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,String code,Model model){
// 获取当前用户
Subject subject = SecurityUtils.getSubject();
// 封装用户的登陆数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Session session = subject.getSession();
String sendCode = (String) session.getAttribute("sendCode");
System.out.println("sendCode:"+sendCode);
try{
subject.login(token); // 执行登录方法,如果没有异常就说明OK了
session.setAttribute("loginUser",token.getPrincipal());
if(!sendCode.equals(code)){
model.addAttribute("msg","验证码错误");
return "login";
}
return "index";
}catch (UnknownAccountException uae) {
model.addAttribute("msg","用户名错误");
return "login";
} catch (IncorrectCredentialsException ice) {
model.addAttribute("msg","密码错误");
return "login";
}
}
// 未授权页面
@RequestMapping("/unauth")
@ResponseBody
public String toUnauthorized(){
return "未授权无法访问该页面";
}
// 注销
@RequestMapping("/user/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.removeAttribute("loginUser");
System.out.println("注销了");
return "redirect:/toLogin";
}
// 邮箱验证码发送
@RequestMapping("/sendCode")
@ResponseBody
public String sendCode(){
// 获取当前用户
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
String s = MailUtil.mail1();
session.setAttribute("sendCode",s);
return s;
}
}
Mapper(Mybatis配置)
package com.example.mapper;
import com.example.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
@Mapper
public interface UserMapper {
User queryUserByName(String name);
}
<?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.example.mapper.UserMapper">
<resultMap type="User" id="usermap">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="password" property="pwd"/>
</resultMap>
<select id="queryUserByName" resultType="User" resultMap="usermap">
select * from mybatis.user where name = #{name}
</select>
</mapper>
pojo
package com.example.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private String pwd;
private String perms;
}
Service
package com.example.service;
import com.example.pojo.User;
import java.util.List;
public interface UserService {
User queryUserByName(String name);
}
package com.example.service;
import com.example.mapper.UserMapper;
import com.example.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userMapper;
@Override
public User queryUserByName(String name){
return userMapper.queryUserByName(name);
}
}
工具类 MailUtil
package com.example.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;
import java.util.Random;
@Component
public class MailUtil {
@Autowired(required = false)
private JavaMailSenderImpl mailSender;
private static MailUtil mailUtil;
@PostConstruct
public void init(){
mailUtil = this;
mailUtil.mailSender = this.mailSender;
}
public static String mail1() {
SimpleMailMessage mailMessage = new SimpleMailMessage();
Random random = new Random();
StringBuffer str = new StringBuffer();
for(int i = 0; i < 6; i++){
str.append(random.nextInt(10));
}
String s = str.toString();
System.out.println("邮箱验证码:"+s);
mailMessage.setSubject("验证码");
mailMessage.setText("您的验证码为:"+s+",如非本人操作,请忽略!请勿回复此邮箱");
// 收件人
// mailMessage.setTo("li17718541773@163.com");
mailMessage.setTo("1303205856@qq.com");
// 发件人
mailMessage.setFrom("1303205856@qq.com");
mailUtil.mailSender.send(mailMessage);
System.out.println("发送成功");
return s;
}
public void mail2() throws MessagingException {
// 复杂的邮件发送
MimeMessage mimeMessage = mailSender.createMimeMessage();
// 提供对 HTML 文本内容、图像等内联元素和典型邮件附件的支持(multipart是否支持多文本上传)
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage,true);
// 主题
helper.setSubject("黎明的黑暗");
// 正文(true表示开启html)
helper.setText("<h1 style='color:red'>穿过黑暗时间</h1>",true);
// 附件
helper.addAttachment("2.jpg",new File("C:\\Users\\Lenovo\\Pictures\\Camera Roll\\2.jpg"));
// 每次只能发给一个人
helper.setTo("lo17828541773@163.com");
// helper.setTo("1306305856@qq.com");
// 发件人
helper.setFrom("1303205856@qq.com");
mailSender.send(mimeMessage);
}
}
前台页面
1.index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<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}">添加</a></div>
<div shiro:hasPermission="user:update"><a th:href="@{/user/update}">修改</a></div>
<p><a th:href="@{/user/logout}">注销</a></p>
</body>
</html>
2.login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript" src="../js/jquery-3.6.0.min.js" ></script>
</head>
<body>
<p style="color:red" th:text="${msg}"></p>
<form th:action="@{/login}">
<p>用户名: <input type="text" name="username"></p>
<p> 密码: <input type="text" name="password"></p>
<p><input type="text" name="code"><button id="click" type="button">获取验证码</button></p>
<p><input type="submit" value="登录"></p>
</form>
<script>
$(function (){
$("#click").click(function (){
$.ajax({
type: "GET",
url: "/sendCode",
success: function(res){
console.log(res);
}
})
})
})
</script>
</body>
</html>
配置(application)
1.application.properties
mybatis.type-aliases-package=com.example.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
#邮件发送配置
spring.mail.default-encoding=UTF-8
spring.mail.host=smtp.qq.com
spring.mail.username= 登录邮箱
spring.mail.password=POP3/SMTP服务
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true
1.application.yaml
spring:
datasource:
username: root
password: root
#?serverTimezone=UTC解决时区的报错
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#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.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
相关依赖
<!--thymeleaf模板-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!-- shiro整合spring的包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.8.0</version>
</dependency>
<!--Druid数据源-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--shiro-thymeleaf整合-->
<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-mail</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>