此项目是idea+springboot+mybatis包含不少小知识,代码没粘全!如有需要百度网盘下载地址:
链接:https://pan.baidu.com/s/1kVsIzwca9KlygeRwSbc9cg
提取码:fdza
也可以加Q:1016844010 不会的可以问我!
项目结构:
application.yml
spring:
datasource:
name : shiro
url : jdbc:mysql://localhost:3306/shiro?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username : root
password : 123456
driver-class-name: com.mysql.jdbc.Driver
filters: stat
maxActive: 20
initialSize: 1
maxWait: 60000
minIdle: 1
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: select 'x'
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxOpenPreparedStatements: 20
thymeleaf:
prefix: classpath:/templates/
content-type: text/html
cache: false
mode: LEGACYHTML5
# 驼峰标志属性与数据库字段自动属性映射
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
returnPageInfo: check
params:
count: countSql
kaptcha.properties:
##################################################
kaptcha.border=no
kaptcha.textproducer.font.color=blue
kaptcha.image.width=110
kaptcha.image.height=60
kaptcha.session.key=code
kaptcha.textproducer.char.length=4
kaptcha.textproducer.font.size=5
kaptcha.background.clear.from=green
kaptcha.background.clear.to=white
kaptcha.textproducer.font.names=Arial,Courier
html页面:
<link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
<link href="/css/login.css" rel="stylesheet">
<script src="/js/jquery-1.12.4.min.js"></script>
<script src="/js/bootstrap.min.js?v=3.3.6"></script>
<script src="/js/aexit.js"></script>
<div class="middle-box text-center loginscreen animated fadeInDown">
<div class="cc">
<div>
<h1 class="logo-name"></h1>
</div>
<h4 id="tips" style="color: red;"></h4>
<div class="form-group">
<label class="name">用 户 名<span></span></label>
<input type="text" id="username" name="username" class="form-control" placeholder="用户ID" onKeyPress="IsEnterKeyPress()" required>
</div>
<div class="form-group">
<label class="name">密 码<span></span></label>
<input type="password" id="password" name="password" class="form-control password" placeholder="密码" onKeyPress="IsEnterKeyPress()" required>
</div>
<div class="form-group">
<label class="name">验 证 码<span></span></label>
<div class="col-sm-8" style="padding-left: 0px; padding-right: 0px;">
<input class="form-control code" id="ckaptcha" type="text" name="kaptcha" placeholder="验证码" onKeyPress="IsEnterKeyPress()" required>
</div>
<div class="col-sm-4" style="padding-left: 0px; padding-right: 0px;">
<img src="/kaptcha" id="kaptcha" width="80px" height="40px"/>
</div>
</div>
<button type="submit" onclick="loginFn()" class="btn btn-primary block full-width m-b">登 录</button>
</div>
</div>
<script>
$(function(){
//点击更换验证码
$("#kaptcha").on('click',function(){
$("#kaptcha").attr('src', '/kaptcha?' + Math.floor(Math.random()*100) ).fadeIn();
});
document.body.style.backgroundImage="url(img/bg_img03.jpg)";
$(".logo-name").css("background-image","url(img/logo3.png)");
});
// 按回车
function IsEnterKeyPress(){
var lKeyCode = (navigator.appname=="Netscape")?event.which:window.event.keyCode;
if ( lKeyCode == 13 ){
loginFn();
}
}
//登陆动作
function loginFn (){
var username = $('#username').val();
var password = $('#password').val();
var userValidateCode = $('#ckaptcha').val();
$.ajax({
type: "POST",
data: {
"username":username,
"password":password,
"user_validate_code":userValidateCode
},
url: Aexit.ctxPath + "/doLogin",
dataType: "json",
cache: false,
success: function(data){
if(data.code === 0){
window.location.href = "/home";
}else{
$("#tips").text(data.message);
$("#kaptcha").attr('src', '/kaptcha?' + Math.floor(Math.random()*100) ).fadeIn();
}
},error : function(XMLHttpRequest, textStatus, errorThrown) {
$("#tips").val("网络或服务器出现问题,请稍后再试!");
}
});
}
</script>
LoginController:
package com.example.weiyu.controller;
import com.example.weiyu.config.AjaxCommonObject;
import com.example.weiyu.config.BizCommonException;
import com.example.weiyu.config.ShiroKit;
import com.example.weiyu.entity.User;
import com.example.weiyu.service.UserService;
import com.example.weiyu.util.AuthConstants;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;
@Controller
public class LoginController {
@Autowired
private UserService userService;
@Resource
private DefaultKaptcha captchaProducer;
@RequestMapping(value = "index")
public String index(){
return "login";
}
@RequestMapping(value = "home")
public String home(){
return "layout/index";
}
/**
* 登录验证
*/
@RequestMapping(value="/doLogin",method = RequestMethod.POST)
@ResponseBody
public AjaxCommonObject doLogin(@RequestParam String username, @RequestParam String password,
@RequestParam(value="user_validate_code",required = false) String userValidateCode,
HttpSession session) {
AjaxCommonObject ajaxCommonObject = new AjaxCommonObject();
User user = userService.findUserByName(username.trim());
if (!userValidateCode.equals(session.getAttribute(Constants.KAPTCHA_SESSION_KEY))) {
return new AjaxCommonObject(new BizCommonException(400,"请输入正确的验证码!"));
}
try {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, ShiroKit.md5(password, user.getSalt()));
subject.login(token);
} catch (Exception e) {
return new AjaxCommonObject(new BizCommonException(400,"请输入正确的用户名密码----!"));
}
session.setAttribute(AuthConstants.CURRENT_USER, user);
return ajaxCommonObject;
}
/**
* 获取验证码
*/
@GetMapping(value = "/kaptcha")
public void kaptcha(HttpSession session, HttpServletResponse response) throws Exception {
try (ServletOutputStream out = response.getOutputStream()) {
response.setDateHeader("Expires", 0);
response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
response.addHeader("Cache-Control", "post-check=0, pre-check=0");
response.setHeader("Pragma", "no-cache");
response.setContentType("image/jpeg");
String capText = captchaProducer.createText();
//TODO 如果是前后台分离或分布式部署,这里需要使用SpringSession放到redis里面
//将验证码存到session
session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);
BufferedImage bi = captchaProducer.createImage(capText);
ImageIO.write(bi, "jpg", out);
out.flush();
}
}
}
pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<mybatisplus-spring-boot-starter.version>1.0.4</mybatisplus-spring-boot-starter.version>
<mybatisplus.version>2.1-gamma</mybatisplus.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatisplus-spring-boot-starter</artifactId>
<version>${mybatisplus-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>${mybatisplus.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring3 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring3</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--添加验证码依赖-->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
<exclusions>
<exclusion>
<artifactId>javax.servlet-api</artifactId>
<groupId>javax.servlet</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<filtering>false</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
</build>
登录和权限认证EnceladusShiroRealm:
package com.example.weiyu.common;
import com.example.weiyu.entity.Menu;
import com.example.weiyu.entity.Role;
import com.example.weiyu.entity.User;
import com.example.weiyu.service.MenuService;
import com.example.weiyu.service.RoleService;
import com.example.weiyu.service.UserService;
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.springframework.beans.factory.annotation.Autowired;
import java.util.List;
public class EnceladusShiroRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private MenuService menuService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//权限信息对象authorizationInfo,用来存放查出的用户的所有的角色(role)及权限(permission)
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
String username = (String) principals.getPrimaryPrincipal();
User user = userService.findUserByName(username);
List<Role> sysRole = roleService.selectRole(user.getId());
for (Role role : sysRole) {
authorizationInfo.addRole(role.getName());
List<Menu> sysPermissions=menuService.selectPermission(role.getId());
for (Menu permission : sysPermissions) {
authorizationInfo.addStringPermission(permission.getName());
}
}
return authorizationInfo;
}
/**
* 配置登录认证信息
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
String username = (String) token.getPrincipal();
User user = userService.findUserByName(username);
if (user == null) {
return null;
}
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo (username,user.getPassword(),getName());
return authenticationInfo;
}
}
拦截器ShiroConfig:
package com.example.weiyu.common;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new HashMap<String, String>();
//配置记住我或认证通过可以访问的地址
filterChainDefinitionMap.put("/index", "anon");
filterChainDefinitionMap.put("/applogin", "anon");
filterChainDefinitionMap.put("/home", "anon");
filterChainDefinitionMap.put("/static/**", "anon");//静态资源不拦截
filterChainDefinitionMap.put("/doLogin", "anon");//anon 可以理解为不拦截
/*filterChainDefinitionMap.put("/logout", "anon");//anon 可以理解为不拦截*/
filterChainDefinitionMap.put("/kaptcha", "anon");//anon 可以理解为不拦截
filterChainDefinitionMap.put("/", "anon");
/* filterChainDefinitionMap.put("/**", "authc,myperm");*/
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
/*shiroFilterFactoryBean.setLoginUrl("/");
shiroFilterFactoryBean.setUnauthorizedUrl("/");*/
/* URLPermissionsFilter urlPermissionsFilter = new URLPermissionsFilter();
Map<String, Filter> filtermap = new HashMap<>();
filtermap.put("myperm",urlPermissionsFilter);shiroFilterFactoryBean.setFilters(filtermap);*/
return shiroFilterFactoryBean;
}
@Bean
public EnceladusShiroRealm shiroRealm() {
EnceladusShiroRealm shiroRealm = new EnceladusShiroRealm();
return shiroRealm;
}
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
return securityManager;
}
}
验证码有关CaptchaConfig:
package com.example.weiyu.config;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.Properties;
@Component
@PropertySource(value = {"classpath:kaptcha.properties"})
public class CaptchaConfig {
@Value("${kaptcha.border}")
private String border;
@Value("${kaptcha.textproducer.font.color}")
private String fontColor;
@Value("${kaptcha.image.width}")
private String imageWidth;
@Value("${kaptcha.image.height}")
private String imageHeight;
@Value("${kaptcha.session.key}")
private String sessionKey;
@Value("${kaptcha.textproducer.char.length}")
private String charLength;
@Value("${kaptcha.textproducer.font.names}")
private String fontNames;
@Bean(name = "captchaProducer")
DefaultKaptcha getKaptchaBean() {
DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
Properties properties = new Properties();
properties.setProperty("kaptcha.border", border);
properties.setProperty("kaptcha.textproducer.font.color", fontColor);
properties.setProperty("kaptcha.image.width", imageWidth);
properties.setProperty("kaptcha.image.height", imageHeight);
properties.setProperty("kaptcha.session.key", sessionKey);
properties.setProperty("kaptcha.textproducer.char.length", charLength);
properties.setProperty("kaptcha.textproducer.font.names", fontNames);
Config config = new Config(properties);
defaultKaptcha.setConfig(config);
return defaultKaptcha;
}
}
密码盐ShiroKit:
package com.example.weiyu.config;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import java.util.Random;
public class ShiroKit {
private static final String NAMES_DELIMETER = ",";
/**
* 加盐参数
*/
public final static String hashAlgorithmName = "MD5";
/**
* 循环次数
*/
public final static int hashIterations = 1024;
/**
* shiro密码加密工具类
*
* @param credentials 密码
* @param saltSource 密码盐
* @return
*/
public static String md5(String credentials, String saltSource) {
ByteSource salt = new Md5Hash(saltSource);
return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations).toString();
}
/**
* 获取随机盐值
* @param length
* @return
*/
public static String getRandomSalt(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 获取当前 Subject
*
* @return Subject
*/
public static Subject getSubject() {
return SecurityUtils.getSubject();
}
/**
* 从shiro获取session
*
*/
public static Session getSession() {
return getSubject().getSession();
}
/**
* 获取shiro指定的sessionKey
*
*/
@SuppressWarnings("unchecked")
public static <T> T getSessionAttr(String key) {
Session session = getSession();
return session != null ? (T) session.getAttribute(key) : null;
}
/**
* 设置shiro指定的sessionKey
*
*/
public static void setSessionAttr(String key, Object value) {
Session session = getSession();
session.setAttribute(key, value);
}
/**
* 移除shiro指定的sessionKey
*/
public static void removeSessionAttr(String key) {
Session session = getSession();
if (session != null)
session.removeAttribute(key);
}
/**
* 验证当前用户是否属于该角色?,使用时与lacksRole 搭配使用
*
* @param roleName
* 角色名
* @return 属于该角色:true,否则false
*/
public static boolean hasRole(String roleName) {
return getSubject() != null && roleName != null
&& roleName.length() > 0 && getSubject().hasRole(roleName);
}
/**
* 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。
*
* @param roleName
* 角色名
* @return 不属于该角色:true,否则false
*/
public static boolean lacksRole(String roleName) {
return !hasRole(roleName);
}
/**
* 验证当前用户是否属于以下任意一个角色。
*
* @param roleNames
* 角色列表
* @return 属于:true,否则false
*/
public static boolean hasAnyRoles(String roleNames) {
boolean hasAnyRole = false;
Subject subject = getSubject();
if (subject != null && roleNames != null && roleNames.length() > 0) {
for (String role : roleNames.split(NAMES_DELIMETER)) {
if (subject.hasRole(role.trim())) {
hasAnyRole = true;
break;
}
}
}
return hasAnyRole;
}
/**
* 验证当前用户是否属于以下所有角色。
*
* @param roleNames
* 角色列表
* @return 属于:true,否则false
*/
public static boolean hasAllRoles(String roleNames) {
boolean hasAllRole = true;
Subject subject = getSubject();
if (subject != null && roleNames != null && roleNames.length() > 0) {
for (String role : roleNames.split(NAMES_DELIMETER)) {
if (!subject.hasRole(role.trim())) {
hasAllRole = false;
break;
}
}
}
return hasAllRole;
}
/**
* 验证当前用户是否拥有指定权限,使用时与lacksPermission 搭配使用
*
* @param permission
* 权限名
* @return 拥有权限:true,否则false
*/
public static boolean hasPermission(String permission) {
return getSubject() != null && permission != null
&& permission.length() > 0
&& getSubject().isPermitted(permission);
}
/**
* 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。
*
* @param permission
* 权限名
* @return 拥有权限:true,否则false
*/
public static boolean lacksPermission(String permission) {
return !hasPermission(permission);
}
/**
* 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。与notAuthenticated搭配使用
*
* @return 通过身份验证:true,否则false
*/
public static boolean isAuthenticated() {
return getSubject() != null && getSubject().isAuthenticated();
}
/**
* 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。。
*
* @return 没有通过身份验证:true,否则false
*/
public static boolean notAuthenticated() {
return !isAuthenticated();
}
/**
* 认证通过或已记住的用户。与guset搭配使用。
*
* @return 用户:true,否则 false
*/
public static boolean isUser() {
return getSubject() != null && getSubject().getPrincipal() != null;
}
/**
* 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。用user搭配使用
*
* @return 访客:true,否则false
*/
public static boolean isGuest() {
return !isUser();
}
/**
* 输出当前用户信息,通常为登录帐号信息。
*
* @return 当前用户信息
*/
public static String principal() {
if (getSubject() != null) {
Object principal = getSubject().getPrincipal();
return principal.toString();
}
return "";
}
}