SpringBoot3 интегрирует SpringSecurity для реализации пользовательской фильтрации разрешений интерфейса.
SpringBoot3 интегрирует SpringSecurity для реализации пользовательской фильтрации разрешений интерфейса.

Средства фильтрации интерфейса Разрешения для определенного интерфейса или функций,систему, установив определенные правила из Разрешения,Разрешено только через Аутентификацию личностии иметь соответствующие Разрешенияиз Доступ и операции пользователя или приложения。Эта технология может эффективно защититьсистема Безопасность ресурсов и данных,Предотвратите выполнение вредоносных операций или несанкционированного доступа неавторизованными пользователями или программами. Обычно,Фильтрацию разрешения интерфейса необходимо использовать в сочетании с другими мерами безопасности.,Например, шифрование, аутентификация личности, аудит и т. д.,Для достижения комплексного защитного эффекта.

1. Импортировать зависимости

Прежде чем реализовывать фильтрацию разрешений пользовательского интерфейса, необходимо сначала импортировать зависимости, в первую очередь Родительская зависимость SpringBoot

Язык кода:javascript
копировать
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.1.0</version>
</parent>

Далее идет веб-зависимость Spring Boot, которая обеспечивает самую базовую поддержку интерфейса.

Язык кода:javascript
копировать
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

При реализации входа в систему вам необходимо разработать токен и интегрировать Redis, поэтому вам необходимо добавить следующие зависимости.

Язык кода:javascript
копировать
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.21.3</version>
</dependency>

Наконец, существует зависимость Spring Security, которая используется для реализации контроля разрешений.

Язык кода:javascript
копировать
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Spring Boot 3.1 версия, соответствующая Spring Security Зависит от 6.1.0 Версия,Заброшенный WebSecurityConfigurerAdapter классы, содержимое конфигурации и Spring Security 5 Существуют очевидные различия, а зависимости версий показаны на рисунке ниже.


2. Напишите интерфейс приглашения для входа в систему.

Для реализации пользовательского интерфейса Разрешения фильтрации,Прежде всегоОпределить подсказку Авторизоватьсяинтерфейс,Используется для возврата пользовательских данных при перехвате,Как показано ниже.

Попросите учащихся создать новый контроллер. SecurityController,определите это /zwz/common/needLogin Интерфейс, код следующий.

Язык кода:javascript
копировать
@RestController
@RequestMapping("/zwz/common")
@Api(tags = "Интерфейс")
@Транзакционный
общественный класс SecurityController {

    @RequestMapping(value = "/needLogin", method = RequestMethod.GET)
    @ApiOperation(value = «Данные не возвращены»)
    public Result<Object> needLogin(){
        return ResultUtil.error(401, "Авторизоваться Неверный");    }
}

3. Напишите функцию обработки успешного входа в систему.

Многие студенты задаются вопросом, где находится интерфейс входа в проект Spring Security и как реализовать функцию входа в систему.

существовать Spring Security середина,насВам нужно только настроить интерфейс входа в систему, обратные вызовы при успешном/неуспешном входе в систему, фильтры и т. д.Вот и все,Разработчикам не нужно внимание наавторизоваться конкретной реализации.

Попросите учащихся создать новую AuthenticationSuccessHandler класс, унаследованный от Spring Security из SavedRequestAwareAuthenticationSuccessHandler добрый ,Метод успешного перезвона пользователю Авторизоваться,。

Язык кода:javascript
копировать
@ApiOperation(value = "Войти успешный обратный вызов")
@Slf4j
@Component
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
}

3.1 Определите, сохранять ли логин

Попросите учащихся сначала переписать AuthenticationSuccessHandler добрыйсерединаиз onAuthenticationSuccess метод,这个метод用于выполнитьВойти успешный обратный вызов

Язык кода:javascript
копировать
@Override
@ApiOperation(value = "Войти успешный обратный вызов")
@SystemLog(about = "Авторизоватьсясистема", type = LogType.LOGIN)
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication ac) throws IOException, ServletException {
}

существоватьвыполнить Войти успешный обратный вызовизстарт,Сначала необходимо определить, проверил ли пользователь автоматическую Авторизацию.,Код для проверки авторизации выглядит следующим образом.

Язык кода:javascript
копировать
String saveLogin = request.getParameter(ZwzLoginProperties.SAVE_LOGIN_PRE);
Boolean saveLoginFlag = false;
if(!ZwzNullUtils.isNull(saveLogin) && Objects.equals(saveLogin,"true")){
    saveLoginFlag = true;
}

Таким образом, пользователю стоит ли сохранять Авторизоватьсяиз Сохранить логотипсуществовать saveLoginFlag переменная для последующего сохранения Token использовать.

3.2 Сохранение информации пользователя и разрешений меню

После успешной авторизации пользователя,Необходимо загрузить пользователя из меню,в это времяУчетная запись пользователя и меню должны быть сохранены в кеше.

Пользователю не нужно авторизоваться второй раз.,Вот и всеБыстрое получение данных меню из кэша,Ускорить загрузку пользовательского меню,Уменьшите нагрузку на чтение базы данных

Сначала определите TokenUser добрый,Используется для хранения временной информации о пользователе (аккаунт, меню Разрешения),Код следующий.

Язык кода:javascript
копировать
@ApiOperation(value = "Временный пользователь добрый")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class TokenUser implements Serializable{

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "имя пользователя")
    private String username;

    @ApiModelProperty(value = "иметьизменю Разрешения")    private List<String> permissions;

    @ApiModelProperty(value = "Это автоматически?Авторизоваться")    private Boolean saveLogin;
}

Тогда продолжайте существовать onAuthenticationSuccess Реализуйте метод извлечения меню.

Язык кода:javascript
копировать
List<String> permissionsList = new ArrayList<>();
List<GrantedAuthority> authorities = (List<GrantedAuthority>) ((UserDetails)ac.getPrincipal()).getAuthorities();
for(GrantedAuthority g : authorities){
    permissionsList.add(g.getAuthority());
}
String username = ((UserDetails)ac.getPrincipal()).getUsername();
TokenUser user = new TokenUser(username, permissionsList, saveLoginFlag);

Приведенный выше код вытащит указанного пользователя из меню «Разрешения» и сохранит его в TokenUser Временный пользователь добрый для последующих звонков.

3.3 Обработка единого входа

Одноточечная аутентификация (SSO) — это технология или протокол аутентификации, который позволяет пользователям использовать одну и ту же группу в нескольких приложениях. Учетные данные (такие как имя пользователя и пароль): вам нужно пройти аутентификацию только один раз, чтобы получить доступ ко всем приложениям, что обеспечивает совместное использование информации аутентификации личности между различными приложениями. Эта технология может повысить удобство пользователя, избежать дублирования и снизить нагрузку на управление паролями. Она также может повысить безопасность и снизить риск утечки паролей и атак.

Для общей системы управления поддерживается единая точка «Авторизоваться».

С точки зрения непрофессионала,Единый вход означает, что одна учетная запись позволяет входить в систему только из одного места.,компьютер A При входе в систему, если вы входите в свой компьютер Б, в это время компьютер A будет "вершина" офлайн.

Тогда продолжайте существовать onAuthenticationSuccess Реализуйте единый вход в метод.

Язык кода:javascript
копировать
public static final String HTTP_TOKEN_PRE = "ZWZ_TOKEN_PRE:";

public static final String USER_TOKEN_PRE = "ZWZ_USER_TOKEN:";
Язык кода:javascript
копировать
String oldToken = redisTemplate.opsForValue().get(ZwzLoginProperties.USER_TOKEN_PRE + username);
if(StrUtil.isNotBlank(oldToken)){
    redisTemplate.delete(ZwzLoginProperties.HTTP_TOKEN_PRE + oldToken);
}

Если старый из Token Если у вас еще есть существование, просто оставьте старое Token Удалите, чтобы реализовать функцию единого входа.

3.4 Постоянная информация для входа

наконец,Сохранение пользовательских данных для Redis в, будет Token Вернитесь во внешний интерфейс и сохраните его в Cookie , можно использовать переднюю часть Token Авторизуйтесь и получите доступ к системе без входа в систему.

сначала это Token создано, может быть использовано UUID добрый Вспомогательное поколение,Код следующий.

Язык кода:javascript
копировать
String token = UUID.randomUUID().toString();

инъекция Redis инструментдобрый,Код следующий.

Язык кода:javascript
копировать
import org.springframework.data.redis.core.StringRedisTemplate;

@Autowired
private StringRedisTemplate redisTemplate;

Тогда согласно 3.1 Шаг за шагом Стоит ли сохранять В результате выполняется постоянная обработка, и учащиеся могут настроить Token из префикса USER_TOKEN_PRE

Язык кода:javascript
копировать
public static final String HTTP_TOKEN_PRE = "ZWZ_TOKEN_PRE:";

public static final String USER_TOKEN_PRE = "ZWZ_USER_TOKEN:";
Язык кода:javascript
копировать
if(saveLoginFlag){
    redisTemplate.opsForValue().set(ZwzLoginProperties.USER_TOKEN_PRE + username, token, 30, TimeUnit.DAYS);
    redisTemplate.opsForValue().set(ZwzLoginProperties.HTTP_TOKEN_PRE + token, JSON.toJSONString(user), 30, TimeUnit.DAYS);
}else{
    redisTemplate.opsForValue().set(ZwzLoginProperties.USER_TOKEN_PRE + username, token, 60, TimeUnit.MINUTES);
    redisTemplate.opsForValue().set(ZwzLoginProperties.HTTP_TOKEN_PRE + token, JSON.toJSONString(user), 60, TimeUnit.MINUTES);
}
ResponseUtil.out(response, ResponseUtil.resultMap(true, 200,"Авторизоватьсяуспех", token));

когда пользовательСохранить логин отмечен,системадержать Token 30 дней, то есть пользователи могут существовать в будущем 30 В течение нескольких дней вам не потребуется входить в систему.

Если пользовательСнимите флажок Сохранить логин.,системадержать Token 60 минут, что пользователь сможет существовать в будущем 1 В течение нескольких часов вам не потребуется входить в систему.

Полный код выглядит следующим образом.

Язык кода:javascript
копировать
@ApiOperation(value = "Войти успешный обратный вызов")
@Slf4j
@Component
public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Autowired
    private ZwzLoginProperties tokenProperties;

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final boolean RESPONSE_SUCCESS_FLAG = true;

    private static final int RESPONSE_SUCCESS_CODE = 200;

    private static final String TOKEN_REPLACE_STR_FRONT = "-";

    private static final String TOKEN_REPLACE_STR_BACK = "";

    @Override
    @ApiOperation(value = "Войти успешный обратный вызов")
    @SystemLog(about = "Авторизоватьсясистема", type = LogType.LOGIN)
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication ac) throws IOException, ServletException {
        String saveLogin = request.getParameter(ZwzLoginProperties.SAVE_LOGIN_PRE);
        Boolean saveLoginFlag = false;
        if(!ZwzNullUtils.isNull(saveLogin) && Objects.equals(saveLogin,"true")){
            saveLoginFlag = true;
        }
        List<String> permissionsList = new ArrayList<>();
        List<GrantedAuthority> authorities = (List<GrantedAuthority>) ((UserDetails)ac.getPrincipal()).getAuthorities();
        for(GrantedAuthority g : authorities){
            permissionsList.add(g.getAuthority());
        }
        String token = UUID.randomUUID().toString().replace(TOKEN_REPLACE_STR_FRONT, TOKEN_REPLACE_STR_BACK);
        String username = ((UserDetails)ac.getPrincipal()).getUsername();
        TokenUser user = new TokenUser(username, permissionsList, saveLoginFlag);
        // Определите, следует ли сохранять меню Разрешения
        if(!tokenProperties.getSaveRoleFlag()){
            user.setPermissions(null);
        }
        // 单点Авторизоватьсясуждение        if(tokenProperties.getSsoFlag()){
            String oldToken = redisTemplate.opsForValue().get(ZwzLoginProperties.USER_TOKEN_PRE + username);
            if(StrUtil.isNotBlank(oldToken)){
                redisTemplate.delete(ZwzLoginProperties.HTTP_TOKEN_PRE + oldToken);
            }
        }
        if(saveLoginFlag){
            redisTemplate.opsForValue().set(ZwzLoginProperties.USER_TOKEN_PRE + username, token, tokenProperties.getUserSaveLoginTokenDays(), TimeUnit.DAYS);
            redisTemplate.opsForValue().set(ZwzLoginProperties.HTTP_TOKEN_PRE + token, JSON.toJSONString(user), tokenProperties.getUserSaveLoginTokenDays(), TimeUnit.DAYS);
        }else{
            redisTemplate.opsForValue().set(ZwzLoginProperties.USER_TOKEN_PRE + username, token, tokenProperties.getUserTokenInvalidDays(), TimeUnit.MINUTES);
            redisTemplate.opsForValue().set(ZwzLoginProperties.HTTP_TOKEN_PRE + token, JSON.toJSONString(user), tokenProperties.getUserTokenInvalidDays(), TimeUnit.MINUTES);
        }
        ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_SUCCESS_FLAG,RESPONSE_SUCCESS_CODE,"Авторизоваться 성공", token));
    }
}

4. Напишите функцию обработки ошибок входа в систему.

Попросите учащихся создать новую AuthenticationFailHandler класс, унаследованный от Spring Security из SimpleUrlAuthenticationFailureHandler добрый , используется для обратного вызова при ошибке входа пользователя из метода,.

Язык кода:javascript
копировать
@ApiOperation(value = "Обратный вызов при ошибке входа")
@Slf4j
@Component
public class AuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {
}

4.1 Определите, правильный ли пароль

Попросите учащихся сначала переписать AuthenticationFailHandler добрыйсерединаиз onAuthenticationFailure метод,这个метод用于выполнитьОбратный вызов при ошибке входа

Язык кода:javascript
копировать
@Override
@ApiOperation(value = "Обратный вызов при ошибке входа")
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
}

существоватьвыполнить Обратный вызов при ошибке входиз Начните с определения того, ввел ли пользователь неправильный пароль.

К ошибкам с паролями относятся:

  1. Пароль пользователя введен неверно。(UsernameNotFoundException аномальный)
  2. Пользователь ввел правильный пароль,но без шифрования。(BadCredentialsException аномальный)
Язык кода:javascript
копировать
if (exception instanceof BadCredentialsException || exception instanceof UsernameNotFoundException) {
    recordLoginTime(request.getParameter("username:"));
    String failTimesStr = stringRedisTemplate.opsForValue().get("LOGIN_FAIL_TIMES_PRE:" + request.getParameter("username:"));
    // Время ошибок
    int userFailTimes = 0;
    if(!ZwzNullUtils.isNull(failTimesStr)){
        userFailTimes = Integer.parseInt(failTimesStr);
    }
    int restLoginTime = 10 - userFailTimes;
    if(restLoginTime < 5 && restLoginTime > 0){
        ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"Пароль учетной записи неверен, вы все равно можете попробовать Авторизоваться" + restLoginTime + «Второсортный»);
    } else if(restLoginTime < 1) {
        ResponseUtil.out(response, ResponseUtil.resultMap(false,500, «Повторная попытка превышает лимит, пожалуйста» + 10 + "После разделения Авторизоваться"));    } else {
        ResponseUtil.out(response, ResponseUtil.resultMap(false,500, «Пароль учетной записи неверен»);
    }
}

Настройка по умолчанию 10 Второсортный Авторизоваться试错Второсортный数,Если оно превышает лимитбудетвременный запрет Авторизоваться

в recordLoginTime метод用于查询Авторизоватьсянеудачаиз Второсортный数,Код следующий.

Язык кода:javascript
копировать
@ApiOperation(value = "Количество неудачных запросов на Авторизацию")
public boolean recordLoginTime(String username) {
    String loginFailTimeStr = stringRedisTemplate.opsForValue().get(LOGIN_FAIL_TIMES_PRE + username);
    int loginFailTime = 0;
    // Количество ошибок
    if(!ZwzNullUtils.isNull(loginFailTimeStr)){
        loginFailTime = Integer.parseInt(loginFailTimeStr) + 1;
    }
    stringRedisTemplate.opsForValue().set("LOGIN_FAIL_TIMES_PRE:" + username, loginFailTime + "", 10, TimeUnit.MINUTES);
    if(loginFailTime >= 10){
        stringRedisTemplate.opsForValue().set("userLoginDisableFlag:"+username, "fail", 10, TimeUnit.MINUTES);
        return false;
    }
    return true;
}

4.2 Определите, не удалось ли выполнить аутентификацию системы

Затем определите, является ли это пользовательским исключением. ZwzAuthException,Если это пользовательское исключение,Затем создайте собственное исключение и информацию,Код следующий.

Язык кода:javascript
копировать
if (exception instanceof ZwzAuthException){
    ResponseUtil.out(response, ResponseUtil.resultMap(false,500,((ZwzAuthException) exception).getMsg()));
}

Пользовательское исключение определяется следующим образом.

Язык кода:javascript
копировать
@ApiOperation(value = «пользовательское исключение»)
public class ZwzAuthException extends InternalAuthenticationServiceException {

    private static final long serialVersionUID = 1L;

    private static final String DEFAULT_MSG = "системааутентификация не удалась";

    @ApiModelProperty(value = «Содержимое сообщения об исключении»)
    private String msg;

    public ZwzAuthException(String msg){
        super(msg);
        this.msg = msg;
    }

    public ZwzAuthException(){
        super(DEFAULT_MSG);
        this.msg = DEFAULT_MSG;
    }

    public ZwzAuthException(String msg, Throwable t) {
        super(msg, t);
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

4.3 Определите, отключена ли учетная запись

Система управления обычно поддерживает функцию отключения учетной записи, то есть отключение учетной записи. status Значение устанавливается в определенное состояние, например -1

Затем определите, отключена ли учетная запись ненормально. DisabledException,Если учетная запись отключена, появится соответствующее сообщение.,Код следующий.

Язык кода:javascript
копировать
if (exception instanceof DisabledException) {
    ResponseUtil.out(response, ResponseUtil.resultMap(false,500,"Учетная запись отключена и не может быть использована");
}

Отключен ли пользователь, обычно это поле сущности добрыйиз, например status

4.4 Другая обработка ошибок входа в систему

Если он не попадает в вышеуказанные три ситуации,Затем будет выдано общее сообщение об ошибке.,Код следующий.

Язык кода:javascript
копировать
else {
    ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_FAIL_CODE,"система в данный момент недоступна, повторите попытку позже");
}

Полный код выглядит следующим образом.

Язык кода:javascript
копировать
@ApiOperation(value = "Обратный вызов при ошибке входа")
@Slf4j
@Component
public class AuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {

    @Autowired
    private ZwzLoginProperties tokenProperties;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final String LOGIN_FAIL_TIMES_PRE = "LOGIN_FAIL_TIMES_PRE:";

    private static final String REQUEST_PARAMETER_USERNAME = "username:";

    private static final boolean RESPONSE_FAIL_FLAG = false;

    private static final int RESPONSE_FAIL_CODE = 500;

    @ApiOperation(value = "Количество неудачных запросов на Авторизацию")
    public boolean recordLoginTime(String username) {
        String loginFailTimeStr = stringRedisTemplate.opsForValue().get(LOGIN_FAIL_TIMES_PRE + username);
        int loginFailTime = 0;
        // Количество ошибок
        if(!ZwzNullUtils.isNull(loginFailTimeStr)){
            loginFailTime = Integer.parseInt(loginFailTimeStr) + 1;
        }
        stringRedisTemplate.opsForValue().set(LOGIN_FAIL_TIMES_PRE + username, loginFailTime + "", tokenProperties.getLoginFailMaxThenLockTimes(), TimeUnit.MINUTES);
        if(loginFailTime >= tokenProperties.getMaxLoginFailTimes()){
            stringRedisTemplate.opsForValue().set("userLoginDisableFlag:"+username, "fail", tokenProperties.getLoginFailMaxThenLockTimes(), TimeUnit.MINUTES);
            return false;
        }
        return true;
    }

    @Override
    @ApiOperation(value = "Обратный вызов при ошибке входа")
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
        if (exception instanceof BadCredentialsException || exception instanceof UsernameNotFoundException) {
            recordLoginTime(request.getParameter(REQUEST_PARAMETER_USERNAME));
            String failTimesStr = stringRedisTemplate.opsForValue().get(LOGIN_FAIL_TIMES_PRE + request.getParameter(REQUEST_PARAMETER_USERNAME));
            //Время ошибок
            int userFailTimes = 0;
            if(!ZwzNullUtils.isNull(failTimesStr)){
                userFailTimes = Integer.parseInt(failTimesStr);
            }
            int restLoginTime = tokenProperties.getMaxLoginFailTimes() - userFailTimes;
            if(restLoginTime < 5 && restLoginTime > 0){
                ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_FAIL_CODE, «Пароль учетной записи неверен, вы также можете попробовать Авторизоваться» + restLoginTime + «Второсортный»);
            } else if(restLoginTime < 1) {
                ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_FAIL_CODE, «Повторная попытка превышает лимит, пожалуйста» + tokenProperties.getLoginFailMaxThenLockTimes() + "После разделения Авторизоваться"));            } else {
                ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_FAIL_CODE, «Пароль учетной записи неверен»));
            }
        } else if (exception instanceof ZwzAuthException){
            ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_FAIL_CODE,((ZwzAuthException) exception).getMsg()));
        } else if (exception instanceof DisabledException) {
            ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_FAIL_CODE, «Учетная запись отключена и не может быть использована»));
        } else {
            ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_FAIL_CODE,"система в данный момент недоступна, повторите попытку позже");
        }
    }
}

5. Напишите фильтры

5.1 Фильтрация разрешений на основе токенов

Попросите учащихся создать новую JwtTokenOncePerRequestFilter фильтр, унаследованный от OncePerRequestFilter,переписать doFilterInternal Метод фильтра, код следующий.

Язык кода:javascript
копировать
@ApiOperation(value = «Настроить фильтр Разрешения»)
@Slf4j
public class JwtTokenOncePerRequestFilter extends OncePerRequestFilter {

    private SecurityUtil securityUtil;

    @Autowired
    private RedisTemplateHelper redisTemplate;

    private ZwzLoginProperties zwzLoginProperties;

    private static final boolean RESPONSE_FAIL_FLAG = false;

    private static final int RESPONSE_NO_ROLE_CODE = 401;


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String tokenHeader = request.getHeader(ZwzLoginProperties.HTTP_HEADER);
        if(ZwzNullUtils.isNull(tokenHeader)){
            tokenHeader = request.getParameter(ZwzLoginProperties.HTTP_HEADER);
        }
        if (ZwzNullUtils.isNull(tokenHeader)) {
            filterChain.doFilter(request, response);
            return;
        }
        try {
            UsernamePasswordAuthenticationToken token = getUsernamePasswordAuthenticationToken(tokenHeader, response);
            SecurityContextHolder.getContext().setAuthentication(token);
        }catch (Exception e){
            log.warn("Не удалось выполнить фильтрацию настроенных разрешений" + e);
        }
        filterChain.doFilter(request, response);
    }

    @ApiOperation(value = «Определить, является ли Авторизация недействительной»)
    private UsernamePasswordAuthenticationToken getUsernamePasswordAuthenticationToken(String header, HttpServletResponse response) {
        String userName = null;
        String tokenInRedis = redisTemplate.get(ZwzLoginProperties.HTTP_TOKEN_PRE + header);
        if(ZwzNullUtils.isNull(tokenInRedis)){
            ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_NO_ROLE_CODE,"Статус авторизоваться недействителен, необходимо войти еще раз!"));
            return null;
        }

        TokenUser tokenUser = JSONObject.parseObject(tokenInRedis,TokenUser.class);
        userName = tokenUser.getUsername();
        List<GrantedAuthority> permissionList = new ArrayList<>();
        if(zwzLoginProperties.getSaveRoleFlag()){
            for(String permission : tokenUser.getPermissions()){
                permissionList.add(new SimpleGrantedAuthority(permission));
            }
        } else{
            permissionList = securityUtil.getCurrUserPerms(userName);
        }
        if(!tokenUser.getSaveLogin()){
            redisTemplate.set(ZwzLoginProperties.USER_TOKEN_PRE + userName, header, zwzLoginProperties.getUserTokenInvalidDays(), TimeUnit.MINUTES);
            redisTemplate.set(ZwzLoginProperties.HTTP_TOKEN_PRE + header, tokenInRedis, zwzLoginProperties.getUserTokenInvalidDays(), TimeUnit.MINUTES);
        }
        if(!ZwzNullUtils.isNull(userName)) {
            User user = new User(userName, "", permissionList);
            return new UsernamePasswordAuthenticationToken(user, null, permissionList);
        }
        return null;
    }

    public JwtTokenOncePerRequestFilter(RedisTemplateHelper redis, SecurityUtil securityUtil,ZwzLoginProperties zwzLoginProperties) {
        this.redisTemplate = redis;
        this.securityUtil = securityUtil;
        this.zwzLoginProperties = zwzLoginProperties;
    }
}

以上代码用于суждение用户из Token статус и определить, действителен ли логин пользователя.

5.2 Графическая фильтрация кода проверки

Попросите учащихся создать новую ImageValidateFilter фильтр, унаследованный от OncePerRequestFilter,переписать doFilterInternal Фильтрационный метод.

Сначала необходимо прочитать фильтр и проверить его внутри интерфейса.,Отпустить, если проверка не требуется,Код следующий.

Язык кода:javascript
копировать
Boolean filterFlag = false;
for(String requestURI : captchaProperties.getVerification()){
    if(pathMatcher.match(requestURI, request.getRequestURI())){
        filterFlag = true;
        break;
    }
}
if(!filterFlag) {
    filterChain.doFilter(request, response);
    return;
}

Если определено, что требуется фильтрация кода подтверждения, попробуйте прочитать код подтверждения. ID captchaId и введите значение кода подтверждения code,существовать Redis Делайте оценочные суждения.

Фильтру необходимо определить, является ли код проверки пустым, просроченным и правильным, и, наконец, обработать его. Код выглядит следующим образом.

Язык кода:javascript
копировать
String verificationCodeId = request.getParameter("captchaId");
String userInputCode = request.getParameter("code");
if(ZwzNullUtils.isNull(userInputCode) || ZwzNullUtils.isNull(verificationCodeId)){
    ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_CODE_FAIL_CODE, «Код проверки пуст»));
    return;
}
String codeAnsInRedis = redisTemplate.opsForValue().get(verificationCodeId);
if(ZwzNullUtils.isNull(codeAnsInRedis)){
    ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_CODE_FAIL_CODE, «Срок действия кода проверки истек, его необходимо заполнить заново»));
    return;
}
if(!Objects.equals(codeAnsInRedis.toLowerCase(),userInputCode.toLowerCase())) {
    ResponseUtil.out(response, ResponseUtil.resultMap(RESPONSE_FAIL_FLAG,RESPONSE_CODE_FAIL_CODE,"Код проверки неверен"));
    return;
}
redisTemplate.delete(verificationCodeId);
filterChain.doFilter(request, response);

6. Интеграция класса WebSecurityConfig.

6.1 Конфигурация класса WebSecurityConfig

Попросите учащихся создать WebSecurityConfig добрый,Увеличивать @Configuration аннотация,Определено как настраиваемое,Код следующий.

Язык кода:javascript
копировать
@ApiOperation(value = "SpringSecurityКонфигурациядобрый")
@Configuration
@EnableMethodSecurity
public class WebSecurityConfig {
}

Затем создайте securityFilterChain метод, конфигурация Spring Security。

сначала это Конфигурациябелый список,То есть Конфигурации не нужно перехватывать детали интерфейса.,Код следующий.

Язык кода:javascript
копировать
.requestMatchers("/zwz/dictData/getByType/**","/zwz/file/view/**","/zwz/user/regist","/zwz/common/**","/*/*.js","/*/*.css","/*/*.png","/*/*.ico", "/swagger-ui.html")

затем Конфигурациянамекать Авторизоватьсястраница и Авторизоваться Интерфейс, код следующий.

Язык кода:javascript
копировать
.formLogin().loginPage("/zwz/common/needLogin").loginProcessingUrl("/zwz/login").permitAll()

затем Конфигурация Войти успешный обратный вызовиметь дело сдобрый,Код следующий.

Язык кода:javascript
копировать
.successHandler(authenticationSuccessHandler)

затем Конфигурация Обратный вызов при ошибке входаиметь дело сдобрый,Код следующий.

Язык кода:javascript
копировать
.failureHandler(authenticationFailHandler)

Наконец, настройте фильтры, включая настраиваемые фильтры разрешений и фильтры графического кода проверки. Код выглядит следующим образом.

Язык кода:javascript
копировать
.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(imageValidateFilter, UsernamePasswordAuthenticationFilter.class);

Полный код показан ниже.

Язык кода:javascript
копировать
@ApiOperation(value = "SpringSecurityКонфигурациядобрый")
@Configuration
@EnableMethodSecurity
public class WebSecurityConfig {

    @Autowired
    private ZwzLoginProperties zwzLoginProperties;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    @Autowired
    private AuthenticationFailHandler authenticationFailHandler;

    @Autowired
    private ZwzAccessDeniedHandler zwzAccessDeniedHandler;

    @Autowired
    private ImageValidateFilter imageValidateFilter;

    @Autowired
    private RedisTemplateHelper redisTemplate;

    @Autowired
    private SecurityUtil securityUtil;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http.authorizeHttpRequests().requestMatchers("/zwz/dictData/getByType/**","/zwz/file/view/**","/zwz/user/regist","/zwz/common/**","/*/*.js","/*/*.css","/*/*.png","/*/*.ico", "/swagger-ui.html").permitAll()
                .and().formLogin().loginPage("/zwz/common/needLogin").loginProcessingUrl("/zwz/login").permitAll()
                .successHandler(authenticationSuccessHandler).failureHandler(authenticationFailHandler).and()
                .headers().frameOptions().disable().and()
                .logout()
                .permitAll()
                .and()
                .authorizeHttpRequests()
                .anyRequest()
                .authenticated()
                .and()
                .cors().and()
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling().accessDeniedHandler(zwzAccessDeniedHandler)
                .and()
                .authenticationProvider(authenticationProvider())
                .addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(imageValidateFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        return username -> userDetailsService.loadUserByUsername(username);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService());
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }

    @Bean
    public JwtTokenOncePerRequestFilter authenticationJwtTokenFilter() throws Exception {
        return new JwtTokenOncePerRequestFilter(redisTemplate, securityUtil, zwzLoginProperties);
    }
}

6.2 Тестирование

Попросите учащихся запустить серверную часть проекта и сначала получить к ней доступ с помощью браузера. http://localhost:8081/,система автоматически перенаправляет на страницу авторизации советов,Как показано ниже.

Затем получите доступ к бесплатному интерфейсу входа в систему. http://localhost:8081/zwz/dictData/getByType/sex,Было обнаружено, что данные можно было прочитать нормально.

Наконец, в этой статье SpringBoot3 успешно интегрирован с SpringSecurity и реализована настраиваемая фильтрация разрешений интерфейса.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose