/**
* @program: shiro
* @description: 用户
* @author: Join
* @create: 2018-12-10 22:29
**/
@Data
public class User {
private int uid;
private String username;
private String password;
private Set roles = new HashSet<>();
}
@Data
public class Permission {
private int pid;
private String name;
private String url;
}
@Data
public class Role {
private int rid;
private String rname;
private Set userSet = new HashSet<>();
private Set permissions = new HashSet<>();
}
4.0.0
org.springframework.boot
spring-boot-starter-parent
1.5.6.RELEASE
com.jiaolin
shiromd5
0.0.1-SNAPSHOT
war
shiromd5
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-test
test
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.1
org.springframework.boot
spring-boot-starter-web
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-tomcat
provided
org.apache.shiro
shiro-core
1.2.3
org.apache.shiro
shiro-spring
1.2.3
com.alibaba
druid
1.0.20
org.apache.commons
commons-lang3
3.4
org.springframework
spring-context-support
4.2.3.RELEASE
org.apache.tomcat.embed
tomcat-embed-jasper
javax.servlet
javax.servlet-api
javax.servlet
jstl
org.projectlombok
lombok
1.16.20
provided
org.springframework.boot
spring-boot-maven-plugin
spring-snapshots
Spring Snapshots
https://repo.spring.io/snapshot
true
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
false
spring-snapshots
Spring Snapshots
https://repo.spring.io/snapshot
true
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
false
/*
Navicat Premium Data Transfer
Source Server : localhost
Source Server Type : MySQL
Source Server Version : 50722
Source Host : localhost:3306
Source Schema : apache_shiro
Target Server Type : MySQL
Target Server Version : 50722
File Encoding : 65001
Date: 17/12/2018 12:19:21
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
`pid` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '',
PRIMARY KEY (`pid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, 'add', '');
INSERT INTO `permission` VALUES (2, 'delete', '');
INSERT INTO `permission` VALUES (3, 'edit', '');
INSERT INTO `permission` VALUES (4, 'query', '');
-- ----------------------------
-- Table structure for permission_role
-- ----------------------------
DROP TABLE IF EXISTS `permission_role`;
CREATE TABLE `permission_role` (
`rid` int(11) NOT NULL,
`pid` int(11) NOT NULL,
INDEX `idx_rid`(`rid`) USING BTREE,
INDEX `idx_pid`(`pid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of permission_role
-- ----------------------------
INSERT INTO `permission_role` VALUES (1, 1);
INSERT INTO `permission_role` VALUES (1, 2);
INSERT INTO `permission_role` VALUES (1, 3);
INSERT INTO `permission_role` VALUES (1, 4);
INSERT INTO `permission_role` VALUES (2, 1);
INSERT INTO `permission_role` VALUES (2, 4);
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`rid` int(11) NOT NULL AUTO_INCREMENT,
`rname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`rid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'admin');
INSERT INTO `role` VALUES (2, 'customer');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '',
PRIMARY KEY (`uid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'admin', 'c41d7c66e1b8404545aa3a0ece2006ac');
INSERT INTO `user` VALUES (2, 'demo', '376204ce2cde0c12bc1ac7ff3651bd93');
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`uid` int(11) NOT NULL,
`rid` int(11) NOT NULL,
INDEX `idx_uid`(`uid`) USING BTREE,
INDEX `idx_rid`(`rid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES (1, 1);
INSERT INTO `user_role` VALUES (2, 2);
SET FOREIGN_KEY_CHECKS = 1;
自定义realm类,需要继承 AuthorizingRealm
package com.jiaolin.shiro.config;
import com.jiaolin.shiro.model.Permission;
import com.jiaolin.shiro.model.Role;
import com.jiaolin.shiro.model.User;
import com.jiaolin.shiro.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.apache.shiro.util.ByteSource;
import org.apache.shiro.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @program: shiro
* @description: 自定义认证器
* @author: Join
* @create: 2018-12-10 23:24
**/
public class AuthRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
/**
* @Description: 授权 1 拿到对应的用户,根据用户拿到角色(admin,customer)
* 和权限的名字(增删改查)
* @Param: [principalCollection]
* @return: org.apache.shiro.authz.AuthorizationInfo
* @Author: Join
* @Date: 23:26
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
User user = (User) principal.fromRealm(this.getClass().getName()).iterator().next();
Set roles = user.getRoles();
List roleList = new ArrayList<>();
List permissionList = new ArrayList<>();
if (!CollectionUtils.isEmpty(roles)) {
for (Role role : roles) {
roleList.add(role.getRname());
Set permissions = role.getPermissions();
if (!CollectionUtils.isEmpty(permissions)) {
for (Permission permission : permissions) {
permissionList.add(permission.getName());
}
}
}
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRoles(roleList);
info.addStringPermissions(permissionList);
return info;
}
/**
* @Description: 认证
* @Param: [authenticationToken]
* @return: org.apache.shiro.authc.AuthenticationInfo
* @Author: Join
* @Date: 23:26
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//token携带了用户信息
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//获取前端输入的用户名
String userName = usernamePasswordToken.getUsername();
//根据用户名查询数据库中对应的记录
User user = userService.findUserByUsername(userName);
//当前realm对象的name
String realmName = getName();
//盐值
ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername());
//封装用户信息,构建AuthenticationInfo对象并返回
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, user.getPassword(),
credentialsSalt, realmName);
return authcInfo;
}
}
然后在进行shiro配置
@Configuration
public class ShiroConfig {
/**
* 密码校验规则HashedCredentialsMatcher
* 这个类是为了对密码进行编码的 ,
* 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
* 这个类也负责对form里输入的密码进行编码
* 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher
*/
/**
* @Description: 下面为添加的
* @Param: []
* @return: HashedCredentialsMatcher
* @Author: Join
* @Date: 2018/12/13
*/
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//指定加密方式为MD5
credentialsMatcher.setHashAlgorithmName("MD5");
//加密次数
credentialsMatcher.setHashIterations(1024);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
@Bean("authRealm")
@DependsOn("lifecycleBeanPostProcessor")//可选
public AuthRealm authRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
AuthRealm authRealm = new AuthRealm();
authRealm.setAuthorizationCachingEnabled(false);
authRealm.setCredentialsMatcher(matcher);
return authRealm;
}
/**
* 定义安全管理器securityManager,注入自定义的realm
* @param authRealm
* @return
*/
@Bean("securityManager")
public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(authRealm);
return manager;
}
/**
* 定义shiroFilter过滤器并注入securityManager
* @param securityManager
* @return
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
bean.setLoginUrl("/login");
bean.setSuccessUrl("/index");
bean.setUnauthorizedUrl("/unauthorized");
LinkedHashMap filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/index", "authc");
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/loginUser", "anon");
//角色为admin的用户才能访问admin网页
filterChainDefinitionMap.put("/admin", "roles[admin]");
//权限为edit的角色才能访问edit页面
filterChainDefinitionMap.put("/edit", "perms[edit]");
filterChainDefinitionMap.put("/druid/**", "anon");
//另外的页面需要用户进行登录才能访问
filterChainDefinitionMap.put("/**", "user");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
/**
* Spring的一个bean , 由Advisor决定对哪些类的方法进行AOP代理 .
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
/**
* 配置shiro跟spring的关联
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
/**
* lifecycleBeanPostProcessor是负责生命周期的 , 初始化和销毁的类
* (可选)
*/
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
public static void main(String[] args) {
String hashAlgorithName = "MD5";
String password = "123";
int hashIterations = 1024;//加密次数
ByteSource credentialsSalt = ByteSource.Util.bytes("admin");
Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);
System.out.println(obj);
}
}
spring和shiro的配置算完成了,然后在来看看登录与登出的操作怎么写,页面的话,后面在进行写进来。
@Controller
public class UserController {
/**
* @Description: 用户登录 登录成功返回index 失败跳转到login
* @Param: [username, password]
* @return: java.lang.String
* @Author: Join
* @Date: 23:13
*/
@RequestMapping("/loginUser")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password, HttpSession session) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token);
//获取主体
User user = (User) subject.getPrincipal();
//并设置在session中
session.setAttribute("user", user);
return "index";
} catch (Exception e) {
e.printStackTrace();
return "login";
}
}
/**
* @Description: 跳转登录页面
* @Param: []
* @return: java.lang.String
* @Author: Join
* @Date: 23:20
*/
@RequestMapping("/login")
public String login() {
return "login";
}
/**
* @Description: admin登录成功
* @Param: []
* @return: java.lang.String
* @Author: Join
* @Date: 23:20
*/
@RequestMapping("/admin")
@ResponseBody
public String admin() {
return "admin success";
}
/**
* @Description: 跳转到index页面
* @Param: []
* @return: java.lang.String
* @Author: Join
* @Date: 23:20
*/
@RequestMapping("/index")
public String index() {
return "index";
}
/**
* @Description: 登出
* @Param: []
* @return: java.lang.String
* @Author: Join
* @Date: 23:14
*/
@RequestMapping("/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
subject.logout();
}
return "login";
}
/**
* @Description: 授权不成功跳转页面
* @Param: []
* @return: java.lang.String
* @Author: Join
* @Date: 23:19
*/
@RequestMapping("/unauthorized")
public String unauthorized() {
return "unauthorized";
}
/**
* @Description: 编辑成功
* @Param: []
* @return: java.lang.String
* @Author: Join
* @Date: 23:18
*/
@RequestMapping("/edit")
@ResponseBody
public String edit() {
return "edit success";
}
}
获取认证的时候需要调用service,以及dao层,先添出来,接口就不添了。
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User findUserByUsername(String name) {
return userMapper.findUserByUsername(name);
}
}
public interface UserMapper {
User findUserByUsername(String username);
}
最后的话,在项目的properties进行全局配置,以及页面的布置,
## database ##
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/apache_shiro?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
server.port=8081
## mybatis ##
mybatis.mapper-locations=mapper/*.xml
mybatis.type-aliases-package=com.jiaolin.shiro.model
## jsp ##
spring.mvc.view.prefix=/page/
spring.mvc.view.suffix=.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
index
欢迎你登录 ${user.username}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Login
欢迎登陆
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
UnAuthorized
UnAuthorized
以上的这些都是JSP页面,为了简便,全部打上了html标签了。springboot项目中默认页面是放到webapp中,和resources同级,懂一点常识的人都知道,这上面的博客为一篇最简单的博客,没做多大的校验,也没有所谓的弄redis集群版,后续会连续发布其余的整合,也是相对较简单的入门篇,源码的话,就不看了,虽然看懂一点点,毕竟我也是一菜鸟,经过本人测试是OK的,爱学习的人欢迎留言咨询,不喜的人也勿喷,如要转载,请经过本人同意。后面做的记录,跟博客没有一点点关联,建议不要看,只是我每天上下班挤地铁时候看的2个小时的书籍做的一点感想。做下记录,以便给我自己脑海中留点记录。
最后,在此对前面2天看过的书籍,学习的精进,其中回忆的两点进行记录,
1-3-12学习法,意思就是说坚持1个月,3个月,12个月,
1-3-10学习法,意思就是坚持1年,3年,10年,这也就是所说的10年学习法,10年之后,你必定是一个不一样的自己,加油。
适当学习法:
输入输出法:以输入和输出的比列来算,3:7的概率来讲,这是所谓的黄金比例,如果没有输出,就减少输入。
对于精英这样用脑不会累写下自我感言
每天设定一个小目标,想办法完成。
完成这个小目标后,在继续指定更大的目标。
完成目标后,给自己一些奖赏。
目标不要不切实际,那样会坚持不下去的。加油。