翻了下,网上的带持久化结构的教程都没用mybatis的版本,也很少有带数据表结构的,就自己动手写了这么一个教程,接下来废话不多说,上干货
项目结构
首先,把数据表准备好
user(用户表)
userId | 用户id |
userName | 用户名 |
password | 用户密码 |
salt | 盐值 |
role(角色表)
id | |
role | 角色 |
roleName | 角色中文名 |
permission(权限表)
id | |
permission | 权限名 |
url | 可访问的url |
RolePermission(角色关联权限表)
id | |
roleid | 角色id |
permissionid | 权限id |
permissionname | 权限名 |
UserRole(用户关联角色表)
id | |
roleid | 角色id |
userid | 用户id |
rolename | 角色中文名 |
其次,把配置和持久层准备好先
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!--排除默认的tomcat-jdbc-->
<exclusion>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jdbc</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</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>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis依赖包-->
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<!--通用mapper-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>1.1.5</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>2.4.2.1-RELEASE</version>
<exclusions>
<exclusion>
<artifactId>shiro-core</artifactId>
<groupId>org.apache.shiro</groupId>
</exclusion>
</exclusions>
</dependency>
application.yml
我用的是开发环境的配置,怎么使用不同环境配置文件,这里就不多说
server:
port: 8080
servlet:
context-path: /national
spring:
datasource:
url: jdbc:mysql://47.658.32.69:3sdzsf306/db_g54uoji_dns?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
username: rvcootdf
password: roocvt123R324OOTvxcf
driver-class-name: com.mysql.jdbc.Driver
hikari:
idle-timeout: 100000 #超时时间
connection-timeout: 1000000 #连接时间
max-lifetime: -1 #连接最长生命周期
minimum-idle: 10 #池中维护的最小空闲连接数
maximum-pool-size: 4000 #最大连接数
thymeleaf:
# 清除缓存
cache: false
prefix: classpath:/html/
suffix: .html #后缀
mode: LEGACYHTML5 #非严格模式
resources:
static-locations: classpath:/static/
mybatis:
mapper-locations: classpath:/mapper/*.xml
type-aliases-package: startApplication.model
logging:
level:
startApplication.dao : debug
dao层(基本上都是mybatis代码生成器的东西,没有额外的自定义的方法)
userMapper(用户)
@Mapper
public interface UserMapper {
int countByExample(UserExample example);
int deleteByExample(UserExample example);
int deleteByPrimaryKey(Integer userid);
/**
* 新增用户
* @param record
* @return
*/
int insert(User record);
/**
* 新增用户
* @param record
* @return
*/
int insertSelective(User record);
/**
* 按条件查询用户
* @param example
* @return
*/
List<User> selectByExample(UserExample example);
/**
* 根据id查询用户
* @param userid
* @return
*/
User selectByPrimaryKey(Integer userid);
int updateByExampleSelective(@Param("record") User record, @Param("example") UserExample example);
int updateByExample(@Param("record") User record, @Param("example") UserExample example);
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);
}
*/
List<User> selectByExample(UserExample example);
/**
* 根据id查询用户
* @param userid
* @return
*/
User selectByPrimaryKey(Integer userid);
int updateByExampleSelective(@Param("record") User record, @Param("example") UserExample example);
int updateByExample(@Param("record") User record, @Param("example") UserExample example);
int updateByPrimaryKeySelective(User record);
int updateByPrimaryKey(User record);
UserRole(用户角色)
@Mapper
public interface UserRoleMapper {
int countByExample(UserRoleExample example);
int deleteByExample(UserRoleExample example);
int deleteByPrimaryKey(Integer id);
/**
* 插入用户角色
* @param record
* @return
*/
int insert(UserRole record);
/**
* 插入用户角色
* @param record
* @return
*/
int insertSelective(UserRole record);
List<UserRole> selectByExample(UserRoleExample example);
/**
* 根据主键查询用户角色
* @param id
* @return
*/
UserRole selectByPrimaryKey(Integer id);
int updateByExampleSelective(@Param("record") UserRole record, @Param("example") UserRoleExample example);
int updateByExample(@Param("record") UserRole record, @Param("example") UserRoleExample example);
int updateByPrimaryKeySelective(UserRole record);
int updateByPrimaryKey(UserRole record);
}
RolePermissionMapper
@Mapper
public interface RolePermissionMapper {
int countByExample(RolePermissionExample example);
int deleteByExample(RolePermissionExample example);
int deleteByPrimaryKey(Integer id);
/**
* 插入角色权限
* @param record
* @return
*/
int insert(RolePermission record);
/**
* 插入角色权限
* @param record
* @return
*/
int insertSelective(RolePermission record);
/**
* 按条件查询角色权限
* @param example
* @return
*/
List<RolePermission> selectByExample(RolePermissionExample example);
/**
* 根据id查询角色权限
* @param id
* @return
*/
RolePermission selectByPrimaryKey(Integer id);
int updateByExampleSelective(@Param("record") RolePermission record, @Param("example") RolePermissionExample example);
int updateByExample(@Param("record") RolePermission record, @Param("example") RolePermissionExample example);
int updateByPrimaryKeySelective(RolePermission record);
int updateByPrimaryKey(RolePermission record);
}
service层
UserServiceInte
public interface UserServiceInte {
/**
* 根据用户名密码查询用户
* @param userName 用户名
* @param password 密码
* @return
*/
public User getUserByName8Password(String userName,String password);
/**
* 用户名是否存在
* @param userName 用户名
* @return 存在返回true,不存在返回false
*/
User getUser(String userName);
/**
* 创建用户
* @param user 用户
* @return
*/
int insertUser(User user);
}
RoleServiceInte
public interface RoleServiceInte {
/**
* 根据用户id查询角色
* @param userId 用户id
* @return
*/
public List<UserRole> getRoleByUser(int userId);
/**
* 添加用户角色
* @param userRole 用户角色
* @return
*/
boolean insertUserRole(UserRole userRole);
/**
* 获取角色
* @param roleId 角色id
* @return
*/
Role getRoleById(int roleId);
}
PermissionServiceInte
public interface PermissionServiceInte {
/**
* 根据角色id查询权限
* @param roleId 角色id
* @return
*/
public List<RolePermission> getPermissionByRole(int roleId);
}
实现类例子
@Service
public class RoleServiceImpl implements RoleServiceInte {
@Autowired(required = false)
private RoleMapper roleMapper;
@Autowired(required = false)
private UserRoleMapper userRoleMapper;
@Override
public List<UserRole> getRoleByUser(int userId) {
UserRoleExample userRoleExample = new UserRoleExample();
UserRoleExample.Criteria criteria = userRoleExample.createCriteria();
criteria.andUseridEqualTo(userId);
return userRoleMapper.selectByExample(userRoleExample);
}
@Override
public boolean insertUserRole(UserRole userRole) {
try {
userRoleMapper.insertSelective(userRole);
}catch (Exception e){
e.printStackTrace();
}
return false;
}
@Override
public Role getRoleById(int roleId) {
return roleMapper.selectByPrimaryKey(roleId);
}
}
项目配置和持久层都准备好了,接下来我们写shiro的配置
shiroconfig(shiro配置类)
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import startApplication.util.ShiroRealm;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean(name = "lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean(name = "shiroRealm")
@DependsOn("lifecycleBeanPostProcessor")
public ShiroRealm shiroRealm() {
ShiroRealm realm = new ShiroRealm();
return realm;
}
@Bean(name = "ehCacheManager")
@DependsOn("lifecycleBeanPostProcessor")
public EhCacheManager ehCacheManager(){
EhCacheManager ehCacheManager = new EhCacheManager();
return ehCacheManager;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
securityManager.setCacheManager(ehCacheManager());//用户授权/认证信息Cache, 采用EhCache 缓存
return securityManager;
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();
// 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionManager.put("/logout", "logout");
// authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
filterChainDefinitionManager.put("/user/**", "authc,roles[user]");
filterChainDefinitionManager.put("/admin/**", "authc,roles[admin]");
filterChainDefinitionManager.put("/login", "anon");
filterChainDefinitionManager.put("/index", "anon");
filterChainDefinitionManager.put("/ajaxLogin", "anon");
filterChainDefinitionManager.put("/register","anon");
filterChainDefinitionManager.put("/**", "authc,roles[user]");//其他资源全部拦截
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);
// 被拦截跳转页面
shiroFilterFactoryBean.setLoginUrl("/index");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/success");
// 未授权界面
shiroFilterFactoryBean.setUnauthorizedUrl("/index");
return shiroFilterFactoryBean;
}
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
daap.setProxyTargetClass(true);
return daap;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
aasa.setSecurityManager(securityManager);
return aasa;
}
}
shiroRealm(shiro获取权限以及校验密码)
public class ShiroRealm extends AuthorizingRealm {
private Logger logger = LoggerFactory.getLogger(ShiroRealm.class);
@Autowired
private UserServiceInte userServiceInte;
@Autowired
private RoleServiceInte roleServiceInte;
@Autowired
private PermissionServiceInte permissionServiceInte;
/**
* 登录认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
logger.info("验证当前Subject时获取到token为:" + token.toString());
System.out.println("当前的登录密码是:"+token.getPassword());
User user = userServiceInte.getUserByName8Password(token.getUsername(), String.valueOf(token.getPassword()));
if (user != null) {
// 若存在,将此用户存放到登录认证info中,无需自己做密码对比,Shiro会为我们进行密码对比校验
List<UserRole> role = roleServiceInte.getRoleByUser(user.getUserid()); //获取用户角色
List<RolePermission> permissions = new LinkedList<>();
for(UserRole userRole:role){
permissions=permissionServiceInte.getPermissionByRole(userRole.getRoleid());
}
List<String> roleStrlist=new ArrayList<String>();用户的角色集合
List<String> perminsStrlist=new ArrayList<String>();//用户的权限集合
for (UserRole userRole : role) {
roleStrlist.add(userRole.getRolename());
}
for (RolePermission rolePermission : permissions) {
perminsStrlist.add(rolePermission.getPermissionname());
}
user.setUserRoleList(roleStrlist);
user.setUserPermission(perminsStrlist);
// 若存在,将此用户存放到登录认证info中,无需自己做密码对比,Shiro会为我们进行密码对比校验
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
System.out.println("验证通过");
return info;
}else {
System.out.println("验证不通过");
return null;
}
}
/**
* 权限认证
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
logger.info("##################执行Shiro权限认证##################");
User user = (User) principalCollection.getPrimaryPrincipal();
if (user != null) {
//权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//用户的角色集合
info.addRoles(user.getUserRoleList());
//用户的权限集合
info.addStringPermissions(user.getUserPermission());
return info;
}
// 返回null的话,就会导致任何用户访问被拦截的请求时,都会自动跳转到unauthorizedUrl指定的地址
return null;
}
}
持久层、密码校验、权限验证都已经准备好了,那我们就可以写controller了
这是加密密码的
@RequestMapping("login")
public Map login(String userName, String password) {
LinkedHashMap<String, Object> resultMap = new LinkedHashMap<>();
int state = 401;
String msg = "登录失败";
try {
User user = userServiceInte.getUser(userName);
String pw = userName + password;
SecurityUtils.getSubject().login( new UsernamePasswordToken(userName,new Md5Hash(pw, user.getSalt(), 3).toString()));
state = 200;
msg = "登陆成功";
} catch (IncorrectCredentialsException e) {
System.out.println("用户名密码错误");
} catch (UnknownAccountException e) {
System.out.println("用户不存在");
}
resultMap.put("state", state);
resultMap.put("msg", msg);
return resultMap;
}
这是不加密密码的
@RequestMapping(value = "login",method = RequestMethod.POST)
public String login(HttpServletRequest request, HttpServletResponse response, User user){
System.out.println("登录");
try {
SecurityUtils.getSubject().login( new UsernamePasswordToken(user.getUserName(),user.getPassword()));
}catch (IncorrectCredentialsException e){
logger.info(user.getUserName()+"用户密码错误!");
return "loginError";
}catch (UnknownAccountException e){
logger.info(user.getUserName()+",该用户名不存在!");
return "loginError";
}
return "/index";
}