文章目录
配置Web环境中出现异常时如何解决不同异常跳转到不同页面方法
@Configuration
public class WebExceptionHandlerConfig {
@Bean
public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
Properties props = new Properties();
//让特定的异常类型和特定的页面对应起来,第二个参数是页面的名称
props.put("org.apache.shiro.authz.UnauthorizedException", "/unauthor");
resolver.setExceptionMappings(props);
return resolver;
}
}
对外共享文件夹
在.yml 配置文件设置上传文件 抵达的文件夹路径
bootdo:
uploadPath: F:/Downdload/
先定义上传路径的类
@Component
@ConfigurationProperties(prefix="bootdo")
public class BootdoConfig {
//上传路径
private String uploadPath;
public String getUploadPath() {
return uploadPath;
}
public void setUploadPath(String uploadPath) {
this.uploadPath = uploadPath;
}
}
在config目录下 新建用于映射类 (继承WebMvcConfigurerAdapter)
@Component
class WebConfigurer extends WebMvcConfigurerAdapter {
@Autowired
BootdoConfig bootdoConfig;
public void addResourceHandlers(ResourceHandlerRegistry registry){
registry.addResourceHandler("/files/**")
.addResourceLocations("file:///"+bootdoConfig.getUploadPath());
}
}
MD5加密
public class MD5Util {
/**
* 验证MD5密码是否正确
*
* @param userPwd 用户提供的密码
* @param dbPwd 数据库的密码
* @param salt 加密的盐(可以是用户名)
* @return
*/
public static boolean validatePwd(String userPwd, String dbPwd, String salt) {
boolean result = false;
String toMd5 = userPwd + salt;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(toMd5.getBytes("UTF-8"));
String encodePwd = Base64.getEncoder().encodeToString(digest);//这是根据用户输入和盐转化后得到的散列密码
return encodePwd.equals(dbPwd);
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
//手动验证
public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String toMd5 ="123456user1";
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(toMd5.getBytes("UTF-8"));
String encodePwd = Base64.getEncoder().encodeToString(digest);
System.out.println(encodePwd);
}
}
ShiroUtil
获取当前环境下登陆的用户对象 此类放进utils包里
public static UserDO getCurrentUser(){
UserDO user = (UserDO)SecurityUtils.getSubject().getPrincipal();
return user;
}
shiro的鉴权与授权(userrealm)
在userrealm包新建MyUserRealm类继承 AuthorizingRealm 会继承2个方法
- 告诉shiro哪个subject有哪些访问权限(doGetAuthorizationInfo )
- 实际的登陆验证(doGetAuthenticationInfo)
public class MyUserRealm extends AuthorizingRealm{
private SysUserService userService;
public void setUserService(SysUserService userService) {
this.userService = userService;
}
/**
* 告诉shiro哪个subject有哪些访问权限
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//查找用户
SysUserDO user = (SysUserDO) SecurityUtils.getSubject().getPrincipal();
List<SysMenuDO> list = userService.findMenusByUser(user.getUsername());
Set<String> permissions = new HashSet<>();//Shiro的权限表示方式1,即使用一个字符串表示
for(SysMenuDO m:list) {
if(m.getPerms()!=null && !(m.getPerms().equals(""))) {
permissions.add(m.getPerms());
}
}
System.out.println("用户: "+user.getUsername()+" 的权限有:"+permissions);
SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
authorInfo.setStringPermissions(permissions);
return authorInfo;
}
/**
* 实际的登陆验证
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("在userRealm中得到了spring的支持下,获取了userService: "+userService);
String username = (String) token.getPrincipal();
String password = new String((char[])token.getCredentials());
SysUserDO user = userService.findByUsername(username);
if(user==null) {
throw new AuthenticationException("用户错误");
}
//启用md5方式判断
if(MD5Util.validatePwd(password, user.getPassword(),username)){
return new SimpleAuthenticationInfo(user,password,getName());
}else {
throw new AuthenticationException("登陆失败");
}
}
}
在config包创建ShiroConfig类 (Shiro的权限基本控制和基础初始化配置)
@Configuration
public class ShiroConfig {
/**
* 实现shiro权限过滤
*/
@Bean("shiroFilter")//bean注解告诉springboot这个方法会返回一个标准的bean
public ShiroFilterFactoryBean getFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
bean.setUnauthorizedUrl("/sys/toLogin");
bean.setLoginUrl("/sys/toLogin");//设定登陆地址,鉴权错误或者没有授权访问时自动跳转到该请求
//权限的全局设置
Map<String, String> auths = new HashMap<>();
auths.put("/sys/toLogin","anon");//如果一个地址不需要权限访问,则设置为anno
auths.put("/sys/doLogin","anon");
auths.put("/project/sysRole/**","authc");//需要授权
auths.put("/**","authc");//其他必须授权
//静态资源的路径要能直接访问
auths.put("/css/**","anon");
auths.put("/fonts/**","anon");
auths.put("/js/**","anon");
bean.setFilterChainDefinitionMap(auths);
return bean;
}
@Bean
public SecurityManager securityManager(MyUserRealm myUserRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myUserRealm);
return securityManager;
}
@Bean
public MyUserRealm mUserRealm(SysUserService userService) {
MyUserRealm userRealm = new MyUserRealm();
//进行一系列的操作比如注入属性
userRealm.setUserService(userService);
return userRealm;
}
/**
* 让shiro参与到spring的bean的管理中来
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 扫描所有的bean寻找通知并将通知器所切入bean中
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
@Bean
public AuthorizationAttributeSourceAdvisor attributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
登陆的控制器
@RequestMapping("doLogin")
@ResponseBody
public Map<String,Object> doLogin(String username,String password,String vcode,HttpServletRequest req ){
Map<String,Object> result = new HashMap<>();
result.put("code","-1");
result.put("msg","未知错误");
//获取Subject对象 SecurityUtils是Shiro提供的类(自带)
Subject subject = SecurityUtils.getSubject();
//封装用户名和密码 UsernamePasswordToken也是Shiro提供的
UsernamePasswordToken token = new UsernamePasswordToken(username,password.toCharArray());
//将封装好的验证信息传递给subject去验证 实际上就是调用了userrealm里的鉴权
try{
subject.login(token);
//获取权限并保存到session中
UserDO user = ShiroUtil.getCurrentUser();
LinkedHashMap<MenuDo,List<MenuDO>> map = menuService.listAllMenusByUserId(user.getUserId);
req.getSession().setAttribute("menus",map);//将数据传入session中
if(subject.isAuthenticated()){
//通过验证
result.put("code","0");
result.put("msg","success");
}catch(AuthenticationException ex){ //userrealm里的鉴权可能会抛出异常信息
result.put("code","1");
result.put("msg",ex.getMessage());
}
return result;
}
}
登陆的页面
用户名:<input type="text" name="username" id="username"><br/>
密码:<input type="password" name="password" id="password"><br/>
<input type="button" value="登陆" onclick="doLogin()">
<script type="text/javascript">
function doLogin(){
var user = $("#username").val();
var pass =$("#password").val();
$.ajax({
url:"/sys/doLogin",
type:"post",
data:{username:user,password:pass},
success:function(data){
if(data.code==0){
alert('登陆成功')
}else{
alert('登陆失败'+data.msg);
}
}
});
}
</script>
在MenuService业务类创建listAllMenusByUserId: 按照页面组件特点生成所有对应用户菜单集合
- 通过用户id 查找出对应的权限集合 父级菜单与子级菜单
/**
* 按照也没组件的特点生成所有对应用户菜单的集合
*/
@Autowired
private SysUserDao sysUserDao;
@Autowired
private SysMenuDao menuDao;
@Autowired
private SysRoleMenuDao roleMenuDao;
@Autowired
private SysUserRoleDao userRoleDao;
@Override
public LinkedHashMap<SysMenuDO, List<SysMenuDO>> listAllMenusByUserId(long userId) {
Map<String, Object> p = new HashMap<String, Object>();
p.put("userId", userId);
List<SysMenuDO> allMenus = new ArrayList<SysMenuDO>();
// 先查询用户角色
List<SysUserRoleDO> roles = userRoleDao.list(p);
p.clear();
for (SysUserRoleDO ur : roles) {
p.put("roleId", ur.getRoleId());
// 查询每一个role对应的权限
List<SysRoleMenuDO> listRm = roleMenuDao.list(p);
p.clear();
for (SysRoleMenuDO rm : listRm) {
SysMenuDO sysMenuDO = menuDao.get(rm.getMenuId());
if (sysMenuDO != null) {
allMenus.add(sysMenuDO);
}
}
}
// 整理结构:按照parentId, order 排序
allMenus.sort(new Comparator<SysMenuDO>() {
@Override
public int compare(SysMenuDO o1, SysMenuDO o2) {
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
if (o1.getParentId().intValue() != o2.getParentId()) {
if (o1.getParentId() == null) {
return -1;
}
if (o2.getParentId() == null) {
return 1;
}
return (int) (o1.getParentId() - o2.getParentId());
}
if (o1.getOrderNum().intValue() != o2.getOrderNum()) {
if (o1.getOrderNum() == null) {
return -1;
}
if (o2.getOrderNum() == null) {
return 1;
}
return o1.getOrderNum() - o2.getOrderNum();
} else {
return (int) (o1.getMenuId() - o2.getMenuId());
}
}
});
// 整理菜单到总结构中
LinkedHashMap<SysMenuDO, List<SysMenuDO>> allMenuInOrder = new LinkedHashMap<>();
Map<Long, SysMenuDO> allParents = new HashMap<>();
for (SysMenuDO m : allMenus) {
if (m == null) {
continue;
}
if (m.getParentId() == null || m.getParentId() == 0) {
allMenuInOrder.put(m, new ArrayList<SysMenuDO>());
allParents.put(m.getMenuId(), m);
} else {
Long parentId = m.getParentId();
SysMenuDO sysMenuDO = allParents.get(parentId);
List<SysMenuDO> list = allMenuInOrder.get(sysMenuDO);
if(list==null) {
continue;//说明该用户有某个子菜单但是没有父菜单,这不显示是合理的,但是这个用户能执行这个操作
}
list.add(m);
}
}
return allMenuInOrder;
}