栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Shiro 验证码篇08

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Shiro 验证码篇08

功能:账号登录时,需要校验验证码,校验失败提示错误 1 github:https://github.com/yezhimincxvxb/shiro/tree/master/shiro08 2 本篇以 注解篇 为基础

shiro08 子工程



    4.0.0

    
        com.yzm
        shiro
        0.0.1-SNAPSHOT
        ../pom.xml 
    

    shiro08
    0.0.1-SNAPSHOT
    jar
    shiro08
    Demo project for Spring Boot

    
        
            com.yzm
            common
            0.0.1-SNAPSHOT
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



项目结构

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test3?useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai
    username: root
    password: root

mybatis-plus:
  mapper-locations: classpath:/mapper
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        String username = (String) principalCollection.getPrimaryPrincipal();
        User user = userService.lambdaQuery().eq(User::getUsername, username).one();
        List roleIds = Arrays.stream(user.getRIds().split(","))
                .map(Integer::parseInt)
                .collect(Collectors.toList());

        List roles = roleService.listByIds(roleIds);
        Set roleNames = new HashSet<>(roles.size());
        Set permIds = new HashSet<>();
        roles.forEach(role -> {
            roleNames.add(role.getRName());
            Set collect = Arrays.stream(
                    role.getPIds().split(",")).map(Integer::parseInt).collect(Collectors.toSet());
            permIds.addAll(collect);
        });

        List permissions = permissionsService.listByIds(permIds);
        List permNames = permissions.stream().map(Permissions::getPName).collect(Collectors.toList());

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.addRoles(roleNames);
        authorizationInfo.addStringPermissions(permNames);
        return authorizationInfo;
    }

    
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
        String username = usernamePasswordToken.getUsername();
        User user = userService.lambdaQuery().eq(User::getUsername, username).one();
        if (user == null) {
            throw new UnknownAccountException();
        }

        return new SimpleAuthenticationInfo(
                user.getUsername(),
                user.getPassword(),
                ByteSource.Util.bytes(user.getUsername() + user.getSalt()),
                getName()
        );
    }
}

ShiroConfig 配置类
package com.yzm.shiro08.config;


import com.yzm.shiro08.service.PermissionsService;
import com.yzm.shiro08.service.RoleService;
import com.yzm.shiro08.service.UserService;
import com.yzm.shiro08.utils.EncryptUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.cookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.cookie;
import org.apache.shiro.web.servlet.Simplecookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import javax.servlet.Filter;
import java.util.linkedHashMap;
import java.util.Map;
import java.util.Properties;

@Configuration
public class ShiroConfig {

    private final UserService userService;
    private final RoleService roleService;
    private final PermissionsService permissionsService;

    public ShiroConfig(UserService userService, RoleService roleService, PermissionsService permissionsService) {
        this.userService = userService;
        this.roleService = roleService;
        this.permissionsService = permissionsService;
    }

    
    @Bean
    public ServletRegistrationBean initServletRegistrationBean() {
        return new ServletRegistrationBean<>(new VerifyServlet(), "/getVerifyCode");
    }

    
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(EncryptUtils.ALGORITHM_NAME);
        hashedCredentialsMatcher.setHashIterations(EncryptUtils.HASH_ITERATIONS);
        //hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }

    
    @Bean
    public MyShiroRealm simpleShiroRealm() {
        MyShiroRealm myShiroRealm = new MyShiroRealm(userService, roleService, permissionsService);
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    
    @Bean
    public cookie simplecookie() {
        Simplecookie cookie = new Simplecookie("rememberMe");
        //设为true后,只能通过http访问,javascript无法访问
        //防止xss读取cookie
        cookie.setHttpOnly(true);
        cookie.setPath("/");
        //存活时间,单位秒;-1表示关闭浏览器该cookie失效
        cookie.setMaxAge(-1);
        return cookie;
    }

    @Bean
    public cookieRememberMeManager rememberMeManager() {
        cookieRememberMeManager rememberMeManager = new cookieRememberMeManager();
        rememberMeManager.setcookie(simplecookie());
        //cookie加密的密钥
        //rememberMeManager.setCipherKey(base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return rememberMeManager;
    }

    
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 配置单个realm
        securityManager.setRealm(simpleShiroRealm());
        securityManager.setRememberMeManager(rememberMeManager());
        return securityManager;
    }

    
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
        defaultAAP.setProxyTargetClass(true);
        return defaultAAP;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setUnauthorizedUrl("/401");

        // 自定义拦截器
        Map filters = new linkedHashMap<>();
        filters.put("verify", new VerifyFilter());
        shiroFilterFactoryBean.setFilters(filters);

        // 拦截url
        Map definitionMap = new linkedHashMap<>();
        definitionMap.put("/home", "anon");
        definitionMap.put("/getVerifyCode", "anon");
        definitionMap.put("/doLogin", "verify");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(definitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public SimpleMappingExceptionResolver simpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        properties.setProperty("org.apache.shiro.authz.UnauthorizedException", "/401");
        properties.setProperty("org.apache.shiro.authz.UnauthenticatedException", "/login");
        simpleMappingExceptionResolver.setExceptionMappings(properties);
        return simpleMappingExceptionResolver;
    }
}
4 接口

HomeController.java

package com.yzm.shiro08.controller;

import com.yzm.common.entity.HttpResult;
import com.yzm.shiro08.entity.User;
import com.yzm.shiro08.service.UserService;
import com.yzm.shiro08.utils.EncryptUtils;
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.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresGuest;
import org.apache.shiro.authz.annotation.RequiresUser;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Controller
public class HomeController {

    private final UserService userService;

    public HomeController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping("login")
    public String login() {
        return "login";
    }

    @GetMapping("home")
    public Object home() {
        return "home";
    }

    @GetMapping("401")
    public Object notRole() {
        return "401";
    }

    @PostMapping("register")
    @ResponseBody
    public Object register(@RequestParam String username, @RequestParam String password) {
        User user = new User();
        user.setUsername(username);
        user.setPassword(password);
        EncryptUtils.encryptPassword(user);
        userService.save(user);
        return HttpResult.ok();
    }

    @PostMapping("doLogin")
    @ResponseBody
    public Object doLogin(@RequestParam String username, @RequestParam String password, boolean rememberMe) {
        try {
            UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
            if (rememberMe) usernamePasswordToken.setRememberMe(true);
            Subject subject = SecurityUtils.getSubject();
            subject.login(usernamePasswordToken);
        } catch (IncorrectCredentialsException ice) {
            return HttpResult.error("password error!");
        } catch (UnknownAccountException uae) {
            return HttpResult.error("username error!");
        }
        return HttpResult.ok();
    }

    @GetMapping("/logout")
    public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException {
        Subject subject = SecurityUtils.getSubject();
        if (subject.isAuthenticated() || subject.isRemembered()) {
            subject.logout();
        }
        response.sendRedirect(request.getContextPath() + "/login");
    }

    @GetMapping("hello")
    @RequiresGuest //登录状态不能访问
    @ResponseBody
    public Object hello() {
        return "hello";
    }

    @GetMapping("list")
    @RequiresUser
    @ResponseBody
    public Object userList() {
        return userService.list();
    }

    @GetMapping("list2")
    @RequiresAuthentication
    @ResponseBody
    public Object userList2() {
        return userService.list();
    }
}

AdminController.java

package com.yzm.shiro08.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/admin")
@RequiresRoles("ADMIN")
public class AdminController {

    @GetMapping
    public Object admin() {
        return SecurityUtils.getSubject().getPrincipal();
    }

    @GetMapping("select")
    @RequiresPermissions("admin:select")
    public Object select() {
        return "Select";
    }

    @GetMapping("create")
    @RequiresPermissions("admin:create")
    public Object create() {
        return "Create";
    }

    @GetMapping("update")
    @RequiresPermissions("admin:update")
    public Object update() {
        return "Update";
    }

    @GetMapping("delete")
    @RequiresPermissions("admin:delete")
    public Object delete() {
        return "Delete";
    }
}

UserController.java

package com.yzm.shiro08.controller;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("//user")
@RequiresRoles(value = {"USER", "ADMIN"}, logical = Logical.OR)
public class UserController {

    @GetMapping
    public Object user() {
        return SecurityUtils.getSubject().getPrincipal();
    }

    @GetMapping("select")
    @RequiresPermissions("user:select")
    public Object select() {
        return "Select";
    }

    @GetMapping("create")
    @RequiresPermissions("user:create")
    public Object create() {
        return "Create";
    }

    @GetMapping("update")
    @RequiresPermissions("user:update")
    public Object update() {
        return "Update";
    }

    @GetMapping("delete")
    @RequiresPermissions("user:delete")
    public Object delete() {
        return "Delete";
    }

    @GetMapping("createAndUpdate")
    //需要同时拥有
    @RequiresPermissions(value = {"user:create", "user:update"}, logical = Logical.AND)
    public Object createAndUpdate() {
        return "Create And Update";
    }

    @GetMapping("createOrUpdate")
    //拥有其中任意一个即可
    @RequiresPermissions(value = {"user:create", "user:update"}, logical = Logical.OR)
    public Object createOrUpdate() {
        return "Create Or Update";
    }
}
5 生成验证码

VerifyServlet.java

package com.yzm.shiro08.config;

import lombok.extern.slf4j.Slf4j;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;


@Slf4j
public class VerifyServlet extends HttpServlet {

    private static final long serialVersionUID = -5051097528828603895L;

    
    private final int width = 100;

    
    private final int height = 30;

    
    private final int codeCount = 4;

    
    private int codeX;

    
    private int codeY;

    
    private int fontHeight;

    
    private final int interLine = 12;

    
    char[] codeSequence = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
            'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
            'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

    
    @Override
    public void init() throws ServletException {
        //width-4 除去左右多余的位置,使验证码更加集中显示,减得越多越集中。
        //codeCount+1 等比分配显示的宽度,包括左右两边的空格
        codeX = (width - 4) / (codeCount + 1);
        //height - 10 集中显示验证码
        fontHeight = height - 10;
        codeY = height - 7;
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, java.io.IOException {
        // 定义图像buffer
        BufferedImage buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // 获取Graphics对象,便于对图像进行各种绘制操作
        Graphics2D gd = buffImg.createGraphics();

        // 背景白色
        gd.setColor(Color.LIGHT_GRAY);
        gd.fillRect(0, 0, width, height);

        // 设置字体,字体的大小应该根据图片的高度来定。
        gd.setFont(new Font("Times New Roman", Font.PLAIN, fontHeight));

        // 画边框。
        gd.setColor(Color.BLACK);
        gd.drawRect(0, 0, width - 1, height - 1);

        // 随机产生干扰线,使图象中的认证码不易被其它程序探测到。
        gd.setColor(Color.green);
        Random random = new Random();
        for (int i = 0; i < interLine; i++) {
            int x = random.nextInt(width);
            int y = random.nextInt(height);
            int xl = random.nextInt(12);
            int yl = random.nextInt(12);
            gd.drawLine(x, y, x + xl, y + yl);
        }

        // randomCode用于保存随机产生的验证码,以便用户登录后进行验证。
        StringBuilder randomCode = new StringBuilder();
        int red, green, blue;
        // 随机产生codeCount数字的验证码。
        for (int i = 0; i < codeCount; i++) {
            // 得到随机产生的验证码数字。
            String strRand = String.valueOf(codeSequence[random.nextInt(36)]);
            // 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。
            red = random.nextInt(255);
            green = random.nextInt(255);
            blue = random.nextInt(255);
            // 用随机产生的颜色将验证码绘制到图像中。
            gd.setColor(new Color(red, green, blue));
            gd.drawString(strRand, (i + 1) * codeX, codeY);
            // 将产生的四个随机数组合在一起。
            randomCode.append(strRand);
        }

        // 将四位数字的验证码保存到Session中。
        HttpSession session = request.getSession();
        session.setAttribute("validateCode", randomCode.toString());
        log.info("验证码:" + randomCode);

        // 禁止图像缓存。
        response.setContentType("image/jpeg");
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-cache");
        response.setDateHeader("Expires", 0);

        // 将图像输出到Servlet输出流中。
        ServletOutputStream sos = response.getOutputStream();
        ImageIO.write(buffImg, "jpeg", sos);
        sos.close();
    }
}
在ShiroConfig中新增
    @Bean
    public ServletRegistrationBean initServletRegistrationBean() {
        return new ServletRegistrationBean<>(new VerifyServlet(), "/getVerifyCode");
    }
6 自定义验证码拦截器

VerifyFilter.java

package com.yzm.shiro08.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;


@Slf4j
public class VerifyFilter extends AccessControlFilter {

    public VerifyFilter() {
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
        HttpServletRequest request = WebUtils.toHttp(servletRequest);
        //这个validateCode是在servlet中存入session的名字
        String validateCode = (String) request.getSession().getAttribute("validateCode");
        //获取用户输入的验证码
        String inputVerify = request.getParameter("verifyCode");
        log.info("用户输入:" + inputVerify);
        if (!validateCode.equalsIgnoreCase(inputVerify)) {
            WebUtils.issueRedirect(servletRequest, servletResponse, "login?verify");
            return false;
        }

        return true;
    }
}
修改login.html



    
    登录页


验证码错误

用户名密码登录

在ShiroConfig#shiroFilter
	@Bean
    public ShiroFilterFactoryBean shiroFilter() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setUnauthorizedUrl("/401");

        // 自定义拦截器
        Map filters = new linkedHashMap<>();
        filters.put("verify", new VerifyFilter());
        shiroFilterFactoryBean.setFilters(filters);

        // 拦截url
        Map definitionMap = new linkedHashMap<>();
        definitionMap.put("/home", "anon");
        definitionMap.put("/getVerifyCode", "anon"); // 放行
        definitionMap.put("/doLogin", "verify"); // 拦截登录接口
        shiroFilterFactoryBean.setFilterChainDefinitionMap(definitionMap);
        return shiroFilterFactoryBean;
    }
7 测试

启动项目,访问/login

输入错误


输入正确

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/333313.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号