编程

当前位置:永利皇宫463登录 > 编程 > Symfony配合数据库进行验证登录,SpringBoot非官方教

Symfony配合数据库进行验证登录,SpringBoot非官方教

来源:http://www.makebuLuo.com 作者:永利皇宫463登录 时间:2019-10-11 09:53

本篇小说将教授使用Spring Security + Mybatis + Mysql数据库完成轻便的报到校验,以致密码加密校验。

转发请表明出处:
http://blog.csdn.net/forezp/article/details/71023817
正文来源方志朋的博客

在既往的厂子方法中,多量的工厂类扩展了类之间的调用信任关系的复杂度,开荒者供给平时去管理这种注重,随着工厂类的反复追加,这种依据关系管理中就难于避免某个大要,导致部分荒唐的发出。另外,在单元测量检验中,由于具体的效率单元被封装在了每贰个工厂类的里边,大家必要深切到类的内部开展测量试验,假设那个效用单元中有依据别的类的某个,我们还索要开展更改,待测验截至后还亟需张开复原。

种类代码:

那篇文篇首要简述怎么着在springboot中验证表单消息。在springmvc工程中,供给检查表单新闻,表单消息验证首要通过注脚的花样。

既是工厂类是作为基础类库剧中人物,那么对于客商定制化的工效就必要从现实的厂子类中脱离出去,但依然保留通用的拍卖流程。也就说,基础类之间的依赖关系被剥离了出来。大家过去开支进程中这种应用层类信任底层基础类的格局被颠倒了回复,变成基础类对应用层类信任,也等于说依赖发生了反转,称为“调整反转”( IOC ,Inversion ofControl)。

1.1 创制数据库
create database user;use user;

创设筑工程程

开创一个springboot工程,由于使用了 web 、thymeleaf、validator、el,引进相应的起步依赖和重视,代码清单如下:

<dependencies> 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-el</artifactId>
        </dependency>
    </dependencies>

小编们平日采用的回调机制正是一种IOC观念的展现。比方视窗系统为控件提供的音讯机制,新闻的收纳、判定、触发等工作由系统自己提供,对于音信管理的有血有肉流程由客商来调整,我们得以通过事件订阅器来为每二个控件设置具体的管理办法。

1.2 创建user_info表,实施sql脚本生成
SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;DROP TABLE IF EXISTS `user_info`;CREATE TABLE `user_info` ( `user_id` int NOT NULL AUTO_INCREMENT COMMENT '主键,自增', `user_name` varchar CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名', `user_password` varchar CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户密码', `user_roles` varchar CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户角色', PRIMARY KEY (`user_id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;INSERT INTO `user_info` VALUES (1, '1', '1', 'ROLE_USER');INSERT INTO `user_info` VALUES (2, 'user', '123456', 'ROLE_USER');INSERT INTO `user_info` VALUES (3, 'user2', '$2a$10$RHWoRd6hPXffZemAD7Gp6ehhf929etTHVGm7JGBeJTNQIgVURnSb.', 'ROLE_USER');SET FOREIGN_KEY_CHECKS = 1;

创造两个PresonForm的Object类

package com.forezp.entity;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
/**
 * Created by fangzhipeng on 2017/4/19.
 */
public class PersonForm {

    @NotNull
    @Size(min=2, max=30)
    private String name;

    @NotNull
    @Min(18)
    private Integer age;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String toString() {
        return "Person(Name: " + this.name + ", Age: " + this.age + ")";
    }
}

其一实体类,在2特性格:name,age.它们分别有表达的笺注:

  • @Size(min=2, max=30) name的长短为2-二21个字符
  • @NotNull 不为空
  • @Min(18)age无法小于18

框架抽象了通用流程,对成熟的、稳固的流水生产线进行了包装。对于开辟者来讲通过框架提供的科班(举个例子子类化抽象类或许完毕相关接口,完成相关的相互合同和接口)就足以将顾客化的代码植入具体的流水生产线中,达成具体景况下客户定制须求。

2.1 修改pom.xml

在上篇文章所需jar包的功底上引进mysql和mybatis相关包。查看完整pom.xml

<!-- Mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId></dependency>

创建 web Controller

@Controller
public class WebController extends WebMvcConfigurerAdapter {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/results").setViewName("results");
    }

    @GetMapping("/")
    public String showForm(PersonForm personForm) {
        return "form";
    }

    @PostMapping("/")
    public String checkPersonInfo(@Valid PersonForm personForm, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            return "form";
        }

        return "redirect:/results";
    }
}

依傍注入(DI,Dependency Injection)是决定反转原则的一种具体完结情势,也正是由此注入的办法实现依赖关系的注脚,框架在运作管理到对应地点时,依照注入的参数去调用具体的拍卖措施。Symfony对依靠注入做了很好的落到实处,整个框架的代码组织便是以这种措施来落实的。上边我们根本讲明Symfony中Security的贯彻进程。

2.2 修改application.yml

在上篇文章resources/application.yml的根底上增加"数据源配置"和"日志配置"

# 端口号server: port: 8080spring: # thymeleaf配置 thymeleaf: enabled: true encoding: UTF-8 mode: HTML servlet: content-type: text/html prefix: classpath:/templates/ suffix: .html # 数据源配置 datasource: url: jdbc:mysql://127.0.0.1:3306/user?useUnicode=true&characterEncoding=utf8&useSSL=false # 修改为自己数据库所在ip地址 driver-class-name: com.mysql.jdbc.Driver username: root # 默认用户名,修改为自己的数据库用户名 password: # 填写自己的密码logging: level: root: INFO org.springframework.web: INFO org.springframework.security: INFO

由此地方的陈设修改后,还亟需有一套完整的CRUD操作,本篇文章仅介绍注册和登陆校验,因而只有注册和登入校验操作。

创建form表单

src/main/resources/templates/form.html:

<html>
    <body>
        <form action="#" th:action="@{/}" th:object="${personForm}" method="post">
            <table>
                <tr>
                    <td>Name:</td>
                    <td><input type="text" th:field="*{name}" /></td>
                    <td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td>
                </tr>
                <tr>
                    <td>Age:</td>
                    <td><input type="text" th:field="*{age}" /></td>
                    <td th:if="${#fields.hasErrors('age')}" th:errors="*{age}">Age Error</td>
                </tr>
                <tr>
                    <td><button type="submit">Submit</button></td>
                </tr>
            </table>
        </form>
    </body>
</html>

Symfony的Security系统作用强盛超过你的想象,不过它的铺排也易于让人发生头眼昏花。上边大家一步步执教怎么着在你的施用中张开安装,从计划防火墙到什么加载顾客以闭门羹访问和获取顾客对象,注重你须求做什么样,不常候单纯需求做一些轻巧的开端化设置,一旦完结那几个,Symfony的Security系统就能够特别灵活和便捷的做事起来。首要步骤如下:

3.1 创造数据库实体类(UserInfo.java)

路径:com/bootcap/session/security/entity/UserInfo.java

package com.bootcap.session.security.entity;/** * 数据库实体类 * 2018-12-10 16:21 */public class UserInfo { private Integer id; private String userName; private String password; private String roles; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getRoles() { return roles; } public void setRoles(String roles) { this.roles = roles; } @Override public String toString() { return "UserInfo{" + ", userName='" + userName + ''' + ", password='" + password + ''' + ", roles='" + roles + ''' + '}'; }}

挂号成功的页面

src/main/resources/templates/results.html:

html>
    <body>
        Congratulations! You are old enough to sign up for this site.
    </body>
</html>

1.安然还是安插文件

#app/config/security.yml  
security:  
    providers:  
        custom_provider:  
                in_memory:  
                    memory: ~  
    firewalls:  
        dev:  
            pattern: ^/(_(profiler|wdt)|css|images|js)  
            security: false  
        default:  
            aynonymous: ~  

重要字“firewalls”(防火墙)是security配置基本,firewalls配置中重要字“dev”不是不可缺少的,重若是为着保障Symfony的开荒者工具不会被security系统所打断。

firewalls中default配置项对具备的U兰德酷路泽L央浼进行了过滤(若无设置pattern意味着将合营全部的UEscortL字符串)关键字aynonymous关注是U奔驰M级L的验证难题。以后以开荒者情势展开主页,你就足以看到眼下的你是以anon.通过验证的,也便是说你只是叁个无名客户。如下图所示:

图片 1

佚名客户访谈

末端会详细介绍怎么着过滤鲜明的UMuranoL也许controller。

security是可配的,在“Security配置参谋”中详尽地介绍了可采用及其表明。

3.2 创造映射文件(UserInfoMapper.java)

: 本文直接运用mybatis的批注格局进行sql编写

路径:com/bootcap/session/security/mapper/UserInfoMapper.java

package com.bootcap.session.security.mapper;import com.bootcap.session.security.entity.UserInfo;import org.apache.ibatis.annotations.*;/** * UserInfo映射类 * 2018-12-10 16:26 * 两个注解区别,网络上说是使用该@Repository注解需要配置xml映射文件,但是小编更改后并未出现异常。 */// @Mapper@Repositorypublic interface UserInfoMapper { /** * 新增 * @param userInfo * @return */ @Insert("insert into user_info(user_name,user_password,user_roles) values(#{userName},#{password}, #{roles})") int insert(UserInfo userInfo); /** * 查询 */ @Select("select user_name as userName,user_password as password,user_roles as roles,user_id as id from user_info where user_name = #{userName}") UserInfo selectByUserName(@Param("userName") String userName);}

演示

最初工程,访谈http://localhost:8080/:

图片 2

倘使您输入A和15,点击 submit:

图片 3

图片 4

如果name 输入N, age为空:

图片 5

借使输入:forezp. 18

图片 6

2.剧中人物机制

证实的进程首要回顾八个方面:

①记名的时候客户接受到分明的顾客角色集结;
②增多代码(U本田UR-VL,调节器)指明可用以访谈的本性(常常为ROLE_ADMIN)。

当客商登陆时,系统会活动得到二个客商剧中人物音讯的集聚,在上面的事例中,是以硬编码的主意存款和储蓄在“security.yml”文件中。假设你是从数据库中加载的客户,那么这个音讯存款和储蓄在表中的一个列中。

如下所示,以“/admin”早先ULANDL访谈权限授予给了剧中人物“ROLE_ADMIN ”,也正是说独有具有此剧中人物的客户本事访问那么些UPRADOL

access_control:
        - { path: ^/admin, role: ROLE_ADMIN }
        - { path: /.*, role: IS_AUTHENTICATED_ANONYMOUSLY }

留意:全部钦命的客户剧中人物必得以“ROLE_”为前缀,不然,Symfony的巴中系统无法正确处理。

角色非常粗大略,就是宗旨的字符串。若是你须要对你的网址中央博物馆客管理成效范围访问,你能够运用“ROLE_BLOG_ADMIN”角色。

只顾:确认保证每叁个顾客最少存有多少个剧中人物,不然你的顾客不可以看到透过认证,平常的老办法是给每三个客商分配ROLE_USER角色。

剧中人物层次

如下所示,ROLE_ADMIN涵盖了ROLE_TEACHER和ROLE_BACKEND八个角色,ROLE_ADMIN同有时候负有了那多少个角色的权杖,不过ROLE_TEACHER和ROLE_BACKEND任何一个都不持有ROLE_ADMIN的权限

    role_hierarchy:
        ROLE_TEACHER:     ROLE_USER
        ROLE_BACKEND:     ROLE_USER
        ROLE_ADMIN:       [ROLE_TEACHER, ROLE_BACKEND]
3.3 成立工作层类(UserInfoService.java)

路径:com/bootcap/session/security/mapper/UserInfoService.java

package com.bootcap.session.security.service;import com.bootcap.session.security.constant.RolesContant;import com.bootcap.session.security.entity.UserInfo;import com.bootcap.session.security.mapper.UserInfoMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.stereotype.Service;/** * service业务层 * 2018-12-10 16:37 */@Servicepublic class UserInfoService { @Autowired UserInfoMapper userInfoMapper; /** * 新增用户 * @param userInfo * @return */ public boolean insert(UserInfo userInfo) { UserInfo userInfo1 = userInfoMapper.selectByUserName(userInfo.getUserName; if (userInfo1 != null){ return false; } userInfo.setRoles(RolesContant.USER); // 加密保存密码到数据库 userInfo.setPassword(new BCryptPasswordEncoder().encode(userInfo.getPassword; int result = userInfoMapper.insert; return result == 1; } /** * 查询用户 * @param username * @return */ public UserInfo selectUserInfo(String username) { return userInfoMapper.selectByUserName; }}

: 由于职业代码应用了常量类,由此须要创设三个枚举类RolesContant.java,内容如下: public static final String USEOdyssey = "ROLE_USER";

仿效资料

https://spring.io/guides/gs/validating-form-input/

3.布署防火墙,举办安全过滤

工程目录appconfigsecurity.yml

# To get started with security, check out the documentation:
# http://symfony.com/doc/current/book/security.html
security:
    encoders:
        ScourgenServicesUserUser: sha256

    # http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
    providers:
        # in_memory:
        #     memory: ~
        custom_provider:
            id: scourgen.user_provider

    role_hierarchy:
        ROLE_TEACHER:     ROLE_USER
        ROLE_BACKEND:     ROLE_USER
        ROLE_ADMIN:       [ROLE_TEACHER, ROLE_BACKEND]
        ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
    firewalls:
        # disables authentication for assets and the profiler, adapt it according to your needs
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false

        main:
            pattern: /.*
            form_login:
                login_path: login
                check_path: login_check
                use_referer: true
                success_handler: scourgen.authentication.success_handler
                failure_handler: scourgen.authentication.failure_handler
            logout:
                path: logout
                success_handler: scourgen.logout.success_handler

            anonymous: true
            # activate different ways to authenticate

            # http_basic: ~
            # http://symfony.com/doc/current/book/security.html#a-configuring-how-your-users-will-authenticate

            # form_login: ~
            # http://symfony.com/doc/current/cookbook/security/form_login_setup.html
    access_control:
        - { path: ^/admin, role: ROLE_ADMIN }
        - { path: /.*, role: IS_AUTHENTICATED_ANONYMOUSLY }

工程目录appconfigservices.yml服务配置文件,框架依照这一个参数举办注入

# Learn more about services, parameters and containers at
# http://symfony.com/doc/current/book/service_container.html
parameters:
#    parameter_name: value

services:
#    service_name:
#        class: AppBundleDirectoryClassName
#        arguments: ["@another_service_name", "plain_value", "%parameter_name%"]
    scourgen.user_provider:
        class:  ScourgenServicesUserUserProvider
        arguments:  ["@service_container"]

    scourgen.authentication.success_handler:
        class: ScourgenWebBundleHandlerAuthenticationSuccessHandler
        parent: security.authentication.success_handler

    scourgen.authentication.failure_handler:
        class: ScourgenWebBundleHandlerAuthenticationFailureHandler
        parent: security.authentication.failure_handler

    scourgen.logout.success_handler:
        class: ScourgenWebBundleHandlerLogoutSuccessHandler
        parent: security.logout.success_handler
3.4 创造调整层(UserController.java)

路径:com/bootcap/session/security/mapper/UserController.java

package com.bootcap.session.security.controller;/** * 2018-12-10 17:02 */@Controllerpublic class UserController { @Autowired UserInfoService userInfoService; @PostMapping("/register") public String doRegister(UserInfo userInfo){ boolean insert = userInfoService.insert; if { return "redirect:sign?success"; } return "redirect:sign?error"; } @GetMapping public String user(@AuthenticationPrincipal Principal principal, Model model){ model.addAttribute("username", principal.getName; return "user/user"; }}

图片 7

: 从流程图中大家得以获取,Spring Security最后采纳UserDetailsService.loadUserByUsername()方法连接数据库获取数据并重回给校验类实行校验,由此大家供给在类型中贯彻该接口。

源码下载

https://github.com/forezp/SpringBootLearning

4.创建顾客实体

顾客实体首要用于Symfony Security实行验证,我们通过从数据库获取客户音讯,并实例化为客商实体,然后交到框架安全模块进行求证。须求专一的客制化User不能够不贯彻AdvancedUserInterface接口。

工程目录ScourgenServicesUserUser.php

<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace ScourgenServicesUser;

use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentSecurityCoreUserAdvancedUserInterface;
use SymfonyComponentSecurityCoreUserEquatableInterface;

/**
 * User is the user implementation used by the in-memory user provider.
 *
 * This should not be used for anything else.
 *
 * @author Fabien Potencier <fabien@symfony.com>
 */
final class User implements AdvancedUserInterface, EquatableInterface, ArrayAccess
{
    protected $data;

    // public function __construct($username, $password, array $roles = array(), $enabled = true, $userNonExpired = true, $credentialsNonExpired = true, $userNonLocked = true)
    // {
    //     if ('' === $username || null === $username) {
    //         throw new InvalidArgumentException('The username cannot be empty.');
    //     }

    //     $this->username = $username;
    //     $this->password = $password;
    //     $this->enabled = $enabled;
    //     $this->accountNonExpired = $userNonExpired;
    //     $this->credentialsNonExpired = $credentialsNonExpired;
    //     $this->accountNonLocked = $userNonLocked;
    //     $this->roles = $roles;
    // }

    public function __toString()
    {
        return $this->getUsername();
    }

    public function __set($name, $value)
    {
        if (array_key_exists($name, $this->data)) {
            $this->data[$name] = $value;
        }
        throw new RuntimeException("{$name} is not exist in current user.");    
    }

    public function __get($name)
    {
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }
        throw new RuntimeException("{$name} is not exist in current user.");        
    }

    public function __isset($name) {
        return isset($this->data[$name]);
    }

    public function __unset($name) {
        unset($this->data[$name]);
    }

    public function offsetExists ($offset) {
        return $this->__isset($offset);

    }
    public function offsetGet ($offset) {
        return $this->__get($offset);
    }

    public function offsetSet ($offset, $value) {
        return $this->__set($offset, $value);
    }

    public function offsetUnset ($offset) {
        return $this->__unset($offset);
    }

    /**
     * {@inheritdoc}
     */
    public function getRoles()
    {
        return $this->roles;
    }

    /**
     * {@inheritdoc}
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * {@inheritdoc}
     */
    public function getSalt()
    {
        return $this->salt;
    }

    public function getId()
    {
        return $this->id;
    }

    /**
     * {@inheritdoc}
     */
    public function getUsername()
    {
        return $this->nickname;
        //return $this->email;
    }

    /**
     * {@inheritdoc}
     */
    public function isAccountNonExpired()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function isAccountNonLocked()
    {
        return !$this->locked;
    }

    /**
     * {@inheritdoc}
     */
    public function isCredentialsNonExpired()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function isEnabled()
    {
        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function eraseCredentials()
    {
    }

    public function isEqualTo(UserInterface $user) {
        if ($this->email !== $user->getUsername()) {
            return false;
        }

        if (array_diff($this->roles, $user->getRoles())) {
            return false;
        }

        if (array_diff($user->getRoles(), $this->roles)) {
            return false;
        }

        return true;
    }

    public function fromArray(array $user)
    {
        $this->data = $user;
        return $this;
    }

    public function toArray()
    {
        return $this->data;
    }

    public function isLogin()
    {
        return empty($this->id) ? false : true;
    }
}
4.1 创造MyUserDetailsService.java类并贯彻UsesrDetailsService
package com.bootcap.session.security.configuration;import com.bootcap.session.security.service.UserInfoService;import com.bootcap.session.security.entity.UserInfo;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.User;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;import java.util.ArrayList;import java.util.List;/** * 自定义登录校验Service * 2018-12-10 17:23 */@Servicepublic class MyUserDetailsService implements UserDetailsService { @Autowired UserInfoService userInfoService; @Override public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException { UserInfo userInfo = userInfoService.selectUserInfo; if (userInfo == null) { throw new UsernameNotFoundException; // 若不存在抛出用户不存在异常 } // 权限字符串转化 List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>(); String[] roles = userInfo.getRoles().split;// 获取后的Roles必须有ROLE_前缀,否则会抛Access is denied无权限异常 for (String role : roles) { simpleGrantedAuthorities.add(new SimpleGrantedAuthority; } // 交给security进行验证并返回 return new User(userInfo.getUserName(), userInfo.getPassword(), simpleGrantedAuthorities); }}

不错文章引用:

  • 更多springboot 教程:springBoot非官方教程 | 小说汇总
  • 更多springcoud 教程:史上最简单易行的 SpringCloud 教程 | 作品汇总

5.编写客制化的顾客驱动

客制化的驱动重若是从数据库中拿走顾客的相干参数,如:客户名、密码、盐值、剧中人物等等,并实例化二个User对象,重临给框架安全模块举行校验。

工程目录ScourgenServicesUserUserProvider.php

<?php
namespace ScourgenServicesUser;


use SymfonyComponentSecurityCoreUserUserInterface;
use SymfonyComponentSecurityCoreUserUserProviderInterface;
use SymfonyComponentSecurityCoreExceptionUnsupportedUserException;
use SymfonyComponentSecurityCoreExceptionUsernameNotFoundException;
use SymfonyComponentSecurityCoreExceptionLockedException;
use ScourgenServicesUserUser;
use ScourgenServicesCommonServiceKernel;


class UserProvider implements UserProviderInterface
{

    public function __construct($container)
    {
        $this->container = $container;
    }
    /**
     * Loads the user for the given username.
     *
     * This method must throw UsernameNotFoundException if the user is not
     * found.
     *
     * @param string $username The username
     *
     * @return UserInterface
     *
     * @see UsernameNotFoundException
     *
     * @throws UsernameNotFoundException if the user is not found
     */
    public function loadUserByUsername($username)
    {
        // 从数据库中查询用户
        $user = $this->getUserByLoginField($username);

        if (empty($user)) {
            throw new UsernameNotFoundException(sprintf('User "%s" not found.', $username));
        }
        $user['currentIp'] = $this->container->get('request')->getClientIp();

        $currentUser = new User();
        $currentUser->fromArray($user);
        ServiceKernel::getInstance()->setCurrentUser($currentUser);

        return $currentUser;
    }

    /**
     * Refreshes the user for the account interface.
     *
     * It is up to the implementation to decide if the user data should be
     * totally reloaded (e.g. from the database), or if the UserInterface
     * object can just be merged into some internal array of users / identity
     * map.
     *
     * @param UserInterface $user
     *
     * @return UserInterface
     *
     * @throws UnsupportedUserException if the account is not supported
     */
    public function refreshUser(UserInterface $user)
    {
        if (! $user instanceof User) {
            throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user)));
        }
        return $this->loadUserByUsername($user->getUsername());
    }

    /**
     * Whether this provider supports the given user class.
     *
     * @param string $class
     *
     * @return bool
     */
    public function supportsClass($class)
    {
        return $class === 'ScourgenServicesUserUser';
    }

    public function getUserByLoginField($key)
    {
        // 判断$key为邮箱、用户名、手机号,这里略去,直接为用户名
        $nickname = $key;
        $sql = 'SELECT * FROM user WHERE nickname = ? LIMIT 1';
        $connection = ServiceKernel::getInstance()->getConnection();
        $user = $connection->fetchAssoc($sql, array($nickname));

        return !$user ? null : UserSerialize::unserialize($user);
    }
}

class UserSerialize
{
    public static function serialize(array $user)
    {
        $user['roles'] = empty($user['roles']) ? '' :  '|' . implode('|', $user['roles']) . '|';
        return $user;
    }

    public static function unserialize(array $user = null)
    {
        if (empty($user)) {
            return null;
        }
        $user['roles'] = empty($user['roles']) ? array() : explode('|', trim($user['roles'], '|')) ;
        return $user;
    }

    public static function unserializes(array $users)
    {
        return array_map(function($user) {
            return UserSerialize::unserialize($user);
        }, $users);
    }
}
4.2 修改WebSecurityConfig.java调用
package com.bootcap.session.security.configuration;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;/** * 2018-12-10 11:03 */@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired MyUserDetailsService myUserDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/","/index").permitAll() // permitAll被允许访问 .antMatchers("/user/**").hasRole// 指定所有user页面需要USER角色才能访问 .and() .formLogin().loginPage.defaultSuccessUrl .and() .logout().logoutUrl("/logout").logoutSuccessUrl; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception {// auth.inMemoryAuthentication() // 在内存中进行身份验证// .passwordEncoder(new BCryptPasswordEncoder// .withUser// .password(new BCryptPasswordEncoder().encode)// .roles;// 修改的地方,将上篇文章的内存验证改为获取数据库,并使用了密码加密 auth.userDetailsService(myUserDetailsService).passwordEncoder(new BCryptPasswordEncoder; }}

6.贯彻贰个大局服务ServiceKernel

Symfony框架本人完结了信任注入机制,大家得以由此在调控器中拿走大肆七个劳动。不过为了完结专门的学问模块与框架的单独,由此大家友好实现了二个专项使用的劳务定位器,便于现在调用大家团结的大局服务。

上边显示的代码中只交给了有的机能完成:

工程目录ScourgenServicesCommonServiceKernel.php

<?php
namespace ScourgenServicesCommon;

class ServiceKernel
{
    private static $_instance;

    protected $currentUser;
    protected $connection;

    public static function create($environment, $debug)
    {
        if (self::$_instance) {
            return self::$_instance;
        }

        $instance = new self();
        $instance->environment = $environment;
        $instance->debug = (Boolean)$debug;
        self::$_instance = $instance;

        return $instance;
    }
    /**
     * 获取服务核心实例
     */
    public static function getInstance()
    {
        if (empty(self::$_instance)) {
            throw new RuntimeException('ServiceKernel未实例化');
        }

        self::$_instance->boot();

        return self::$_instance;
    }

    public function boot()
    {

    }

    public function setCurrentUser($user)
    {
        if (null == $user) {
            throw new RuntimeException('Arguments 1 $user is null');
        }

        $this->currentUser = $user;
        return $this;
    }

    public function getCurrentUser()
    {
        if ((null == $this->currentUser)) {
            throw new RuntimeException('尚未初始化CurrentUser');
        }
        return $this->currentUser;
    }

    public function setConnection($connection)
    {
        if (null == $connection) {
            throw new RuntimeException('Arguments 1 $connection is null');
        }

        $this->connection = $connection;
        return $this;
    }

    public function getConnection()
    {
        if (null == $this->connection) {
            throw new RuntimeException('尚未初始化数据库连接');
        }

        return $this->connection;
    }
}
扩展:对密码进行加密

1、修改WebSecurityConfig.configure()方法

 auth.userDetailsService(myUserDetailsService).passwordEncoder(new BCryptPasswordEncoder;

2、在UserInfoService.insert()方法对password字段进行加密存款和储蓄

 public boolean insert(UserInfo userInfo) { UserInfo userInfo1 = userInfoMapper.selectByUserName(userInfo.getUserName; if (userInfo1 != null){ return false; } userInfo.setRoles(RolesContant.USER); userInfo.setPassword(new BCryptPasswordEncoder().encode(userInfo.getPassword; int result = userInfoMapper.insert; return result == 1; }

7.实例化ServiceKernel

在网址输入文件中对ServiceKernel进行实例化

工程目录webapp_dev.php

...

// 初始化ServiceKernel
$serviceKernel = ServiceKernel::create($kernel->getEnvironment(), $kernel->isDebug());
$serviceKernel->setConnection($kernel->getContainer()->get('database_connection'));
$serviceKernel->getConnection()->exec('SET NAMES UTF8');

// var_dump($kernel->getContainer()->getParameterBag());
// var_dump($request);

$currentUser = new User();
$currentUser->fromArray(array(
    'id' => 0,
    'nickname' => '游客',
    'currentIp' =>  $request->getClientIp(),
    'roles' => array()
));
$serviceKernel->setCurrentUser($currentUser);

...
4.3 修改TemplateConfig.java(用于新扩大的登记页面)
@Configurationpublic class TemplateConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController.setViewName; registry.addViewController.setViewName; registry.addViewController.setViewName; registry.addViewController.setViewName; // 新增注册页面 registry.addViewController.setViewName("register"); }}

8.成功登陆管理钩子

工程目录ScourgenWebBundleHandlerAuthenticationSuccessHandler.php

<?php
namespace ScourgenWebBundleHandler;

use SymfonyComponentSecurityHttpAuthenticationDefaultAuthenticationSuccessHandler;
use SymfonyComponentSecurityCoreExceptionAuthenticationException;
use SymfonyComponentSecurityCoreAuthenticationTokenTokenInterface;
use SymfonyComponentHttpFoundationRequest;
//use ScourgenWebBundleHandlerAuthenticationHelper;

class AuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler
{
    /**
     * {@inheritdoc}
     */
    public function onAuthenticationSuccess(Request $request, TokenInterface $token)
    {
        $userId = $token->getUser()->id;

        // $forbidden = AuthenticationHelper::checkLoginForbidden($userId);

        // if ($forbidden['status'] == 'error') {
        //     $exception = new AuthenticationException($forbidden['message']);
        //     throw $exception;
        // } else {
        //     $this->getUserService()->markLoginSuccess($userId, $request->getClientIp());
        // }

        $sessionId = $request->getSession()->getId();

        if ($request->isXmlHttpRequest()) {
            $response = array(
                'success' => true
            );

            return new JsonResponse($response, 200);
        }

        // if ($this->getAuthService()->hasPartnerAuth()) {
        //     $url = $this->httpUtils->generateUri($request, 'partner_login');
        //     $queries = array('goto' => $this->determineTargetUrl($request));
        //     $url = $url . '?' . http_build_query($queries);
        //     return $this->httpUtils->createRedirectResponse($request, $url);
        // }
        var_dump($token);
        return parent::onAuthenticationSuccess($request, $token);
    }

    public function createToken(Request $request)
    {
        $userLoginToken = $request->cookies->get('U_LOGIN_TOKEN');

        if (empty($userLoginToken)) {
            $userLoginToken = md5($request->getSession()->getId);
            etcookie('U_LOGIN_TOKEN', $userLoginToken, time() + 3600 * 24 * 265);
        }
        return $userLoginToken;
    }
}
4.4 上述配置完毕后,大家需求充实和修改多少个页面用于测量试验

4.4.1 修改login.html(增添跳转到注册连接)

<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"><head> <meta charset="UTF-8"> <title>登录页面</title></head><body><h1>登录页面</h1><div th:if="${param.error}"> 用户名或密码不正确</div><div th:if="${param.logout}"> 你已经退出登录</div><form th:action="@{/login}" method="post"> <div><label> 用户名: <input type="text" name="username"/> </label></div> <div><label> 密&nbsp;&nbsp;&nbsp;码: <input type="password" name="password"/> </label></div> <div> <input type="submit" value="登录"/> <a th:href="@{/sign}">注册</a> </div></form></body></html>

4.4.2 新扩展register.html注册页面

<html xmlns:th="http://www.thymeleaf.org"><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>注册</title></head><body><div > <h1>注册页面</h1> <form th:action="@{/register}" method="post"> <div> 账号: <input type="text" name="userName" placeholder="账号"> </div> <div> 密码: <input type="password" name="password" placeholder="密码"> </div> <br> <div th:if="${param.error}"> <p>注册失败,账号已存在!</p> </div> <div th:if="${param.success}"> <p>注册成功,可以登录了!</p> </div> <div> <button type="submit" >注册</button> <a href="/login">返回登录页面</a> </div> </form></div></body></html>

4.4.3 新添user.html(用于登入后跳转的页面)路线:templates/user/user.html

<html xmlns:th="http://www.thymeleaf.org"><head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>用户界面</title></head><body><div style="margin-top: 60px"> <div style="text-align: center; margin-top: 10%"> <p th:text="${username}" style="margin-top: 25px; font-size: 20px; color: crimson"></p> <form th:action="@{/logout}" method="post"> <button style="margin-top: 20px">退出登录</button> </form> </div></div></body></html>

时现今日,基本修改已经收尾,那时候能够运行Application.java运转运营了?答案:不能!!!

: 本项目标起步类是在app包下,因而大家配备的mapper映射文件的@Repository注脚为mybatis的注脚,所以boot不会自行注入,由此供给投入@MapperScan(basePackages = "com.bootcap.session.security.mapper")

package com.bootcap.session.security.app;/** * 启动类 * 2018-12-10 11:03 */@SpringBootApplication@ComponentScan(basePackages = {"com.bootcap.session.security"})@MapperScan(basePackages = "com.bootcap.session.security.mapper")public class Application { public static void main(String[] args) { SpringApplication.run(Application.class,args); }}

6.1 项目运行后,浏览器访谈:localhost:8080/user,会发掘一向跳转到登入页

图片 8

6.2 近日大家未有账号,点击注册开关举办登记,注册好后。大家运用登记的账号实行登入,能够不奇怪进入user页面

图片 9

8.登陆失利管理钩子

工程目录ScourgenWebBundleHandlerAuthenticationFailureHandler.php

<?php
namespace ScourgenWebBundleHandler;

use SymfonyComponentSecurityCoreExceptionAuthenticationException;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationJsonResponse;


class AuthenticationFailureHandler extends DefaultAuthenticationFailureHandler
{
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
    {
        $request->getSession()->set('_target_path', $request->request->get('_target_path'));

        if ($exception->getMessage() === 'Bad credentials.') {
            $message = '用户名或密码错误';
        } else {
            goto end;
        }

        // 连续登陆时间间隔 登陆次数上限检测

        $default = array(
            'temporary_lock_enabled' => 0,
            'temporary_lock_allowed_times' => 5,
            'ip_temporary_lock_allowed_times' => 20,
            'temporary_lock_minutes' => 20,
        );

        $exception = new AuthenticationException($message);


        end:

        if ($request->isXmlHttpRequest()) {
            $content = array(
                'success' => false,
                'message' => $message
            );

            return new JsonResponse($content ,400);
        }

        return parent::onAuthenticationFailure($request, $exception);
    }
}

9.成功脱离钩子

工程目录ScourgenWebBundleHandlerLogoutSuccessHandler.php

<?php
namespace ScourgenWebBundleHandler;

use SymfonyComponentSecurityHttpLogoutDefaultLogoutSuccessHandler;
use SymfonyComponentHttpFoundationRequest;

class LogoutSuccessHandler extends DefaultLogoutSuccessHandler
{
    public function onLogoutSuccess(Request $request)
    {
        $goto = $request->request->get('goto');

        if (!$goto) {
            $goto = 'login';
        }

        $this->targetUrl = $this->httpUtils->generateUri($request, $goto);
        setcookie('REMEMBERME');

        return parent::onLogoutSuccess($request);
    }
}
7.1 创立八个自定义Filter过滤器(BeforeFilter.java和BeforeFilter.java)

路径:com/bootcap/session/security/filter

package com.bootcap.session.security.filter;/** * BeforeFilter * 2018-12-12 10:44 */public class BeforeFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("在 UsernamePasswordAuthenticationFilter 前添加 BeforeLoginFilter"); // 继续调用 Filter 链 filterChain.doFilter(servletRequest, servletResponse); }}

package com.bootcap.session.security.filter;/** * AfterCsrfFilter * 2018-12-12 10:46 */public class AfterCsrfFilter extends GenericFilterBean { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("在 CsrfFilter 后添加 AfterCsrfFilter"); filterChain.doFilter(servletRequest,servletResponse); }}
7.2 在WebSecurityConfig.configure()方法中投入调用
@Override protected void configure(HttpSecurity http) throws Exception { ... 省略部分代码 ... // 在 UsernamePasswordAuthenticationFilter 前添加 BeforeLoginFilter http.addFilterBefore(new BeforeFilter(), UsernamePasswordAuthenticationFilter.class); // 在 CsrfFilter 后添加 AfterCsrfFilter http.addFilterAfter(new AfterCsrfFilter(), CsrfFilter.class); }
7.3 运转程序并访谈放肆可访谈页面,我们能够的到四个过滤器的施行各种
2018-12-12 10:49:34.607 INFO 1060 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet'2018-12-12 10:49:34.607 INFO 1060 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started2018-12-12 10:49:34.611 INFO 1060 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization completed in 4 ms在 CsrfFilter 后添加 AfterCsrfFilter在 UsernamePasswordAuthenticationFilter 前添加 BeforeLoginFilter在 CsrfFilter 后添加 AfterCsrfFilter在 UsernamePasswordAuthenticationFilter 前添加 BeforeLoginFilter

上一篇:Spring Security 入门教程 - 轻便的记名认证下一篇:Spring Security 入门教程- 基于登陆认证记住本身实例

本文由永利皇宫463登录发布于编程,转载请注明出处:Symfony配合数据库进行验证登录,SpringBoot非官方教

关键词: