GrailsSpringSecurity

GrailsSpringSecurity,grails,spring-security,Grails,Spring Security,我正在尝试创建一个登录页面,该页面将根据用户的角色登录到特定帐户,但由于某些原因,spring security从未重新验证用户名和密码 这是登录控制器 package login_page 导入grails.plugin.springsecurity.userdetails.DefaultPostAuthenticationChecks 导入org.springframework.security.access.annotation.securied @安全('permitAll') 类Lo

我正在尝试创建一个登录页面,该页面将根据用户的角色登录到特定帐户,但由于某些原因,spring security从未重新验证用户名和密码

这是登录控制器

package login_page
导入grails.plugin.springsecurity.userdetails.DefaultPostAuthenticationChecks 导入org.springframework.security.access.annotation.securied

@安全('permitAll') 类LoginController扩展了grails.plugin.springsecurity.LoginController{

PersonService personService

def index() {
    if (isLoggedIn()) {
        redirect uri: conf.successHandler.defaultTargetUrl
    }
    else {
        redirect action: 'auth', params: params
    }
}
def auth() {

    def conf = getConf()

    if (isLoggedIn()) {
        redirect uri: conf.successHandler.defaultTargetUrl
        return
    }

    String postUrl = request.contextPath + conf.apf.filterProcessesUrl
    render view: 'index', model: [postUrl: postUrl,
                                 rememberMeParameter: conf.rememberMe.parameter,
                                 usernameParameter: conf.apf.usernameParameter,
                                 passwordParameter: conf.apf.passwordParameter,
                                ]
}
}

成功uri为/person/LoginPage

LoginPage方法是这样的

   @Secured(['ROLE_USER','ROLE_ADMIN','ROLE_SUPERADMIN'])
def LoginPage() {
      refreshCurrentUser()
    if (currentPerson == null) {
        notFound()
    }else {
        if(currentPerson.getAuthorities()[0].getAuthority()=="ROLE_SUPERADMIN"){
            redirect(controller:'superAdmin', action: 'superAdminShow', id: currentPerson.id)
        }
       else if(currentPerson.getAuthorities()[0].getAuthority()=="ROLE_ADMIN"){
            redirect(controller:'admin', action: 'adminShow', id: currentPerson.id)
        }
        else if(currentPerson.getAuthorities()[0].getAuthority()=="ROLE_USER"){
            redirect(action: 'show', id: currentPerson.id)
        }
    }
}

这是一个非常简单的安全配置,我为一个愚蠢的演示动态地组合在一起(一个反应式JavaRESTAPI)。我绝对不是一个安全专家,但它可以让你对所涉及的事情有一个概念

import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;

import XXXXXXXX.Permissions;
import XXXXXXXXX.UserPermissions;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity.AuthorizeExchangeSpec;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import reactor.core.publisher.Mono;


@Configuration
@EnableWebFluxSecurity
public class ApiSecurityConfiguration implements ReactiveAuthenticationManager{

    @Autowired
    Permissions permissions;
    @Autowired
    RedisTemplate<String, Object> redisCache;

    private static final Logger logger = LoggerFactory.getLogger(ApiSecurityConfiguration.class);
    private UserPermissions userPermissionTable;

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        AuthorizeExchangeSpec authorize;
        http.formLogin().disable();
        http.csrf().disable();
        http.logout().disable();å
        http.httpBasic().disable();
        http.httpBasic().
        authenticationManager(this::authenticate);
        authorize = http.authorizeExchange();
        authorize.anyExchange().access(this::check);

        return http.build();
    }

    private Mono<AuthorizationDecision> check(Mono<Authentication> authentication, AuthorizationContext context) {
        return authentication.map(a ->this.checkAuthorizations(a, context)).map(granted -> new AuthorizationDecision(granted));
    }

    private boolean checkAuthorizations(Authentication a, AuthorizationContext context){
        boolean ret = false;
        String name = a.getName();
        if (a.isAuthenticated()){
            logger.info(String.format("FOUND %s, authorizing...", name));
            ret = userPermissionTable.canAccess("XXXX", context.getExchange().getRequest().getPath().value(), context.getExchange().getRequest().getMethodValue());
            logger.info(String.format("%s access granted: %B", name, ret));
        }
        return ret;
    }

    @Override
    public Mono<Authentication> authenticate(Authentication authentication) {
        CompletableFuture<UserPermissions> cup;
        Authentication auth;
        String name = null;

        auth = authentication;
        auth.setAuthenticated(false);
        try {
            name = authentication.getName();
            logger.info(String.format("Looking %s in cache...", name));
            userPermissionTable = (UserPermissions)redisCache.opsForValue().get(name);
            if (userPermissionTable == null){
                logger.info(String.format("NOT in cache, authenticating: %s ...", name));
                cup = permissions.getPermissionsForUser(name, authentication.getCredentials().toString());
                userPermissionTable = cup.get(1000, TimeUnit.MILLISECONDS);
                redisCache.opsForValue().set(name, userPermissionTable, userPermissionTable.getTTL(), TimeUnit.MINUTES);
                auth = new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), null);
                logger.info(String.format("Authenticated: %s", name));
            }
            else{
                auth = new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), null);
                redisCache.expire(name, userPermissionTable.getTTL(), TimeUnit.MINUTES);
            }
        } catch (Exception e) {
            logger.info(String.format("FAILED to authenticate: %s", name));
        }
        return Mono.just(auth);
    }
}
import org.springframework.context.annotation.Configuration;
导入org.springframework.data.redis.core.RedisTemplate;
导入XXXXXXXX.权限;
导入xxxxxxxx.UserPermissions;
导入java.util.concurrent.CompletableFuture;
导入java.util.concurrent.TimeUnit;
导入org.slf4j.Logger;
导入org.slf4j.LoggerFactory;
导入org.springframework.beans.factory.annotation.Autowired;
导入org.springframework.context.annotation.Bean;
导入org.springframework.security.authentication.ReactiveAuthenticationManager;
导入org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
导入org.springframework.security.authorization.AuthorizationDecision;
导入org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
导入org.springframework.security.config.web.server.ServerHttpSecurity;
导入org.springframework.security.config.web.server.ServerHttpSecurity.authorizeExchangeSpect;
导入org.springframework.security.core.Authentication;
导入org.springframework.security.web.server.SecurityWebFilterChain;
导入org.springframework.security.web.server.authorization.AuthorizationContext;
导入reactor.core.publisher.Mono;
@配置
@启用WebFluxSecurity
公共类ApiSecurityConfiguration实现ReactiveAuthenticationManager{
@自动连线
权限;
@自动连线
redisplate-redisCache;
私有静态最终记录器Logger=LoggerFactory.getLogger(ApiSecurityConfiguration.class);
私有用户权限userPermissionTable;
@豆子
公共安全WebFilterChain springSecurityFilterChain(ServerHttpSecurity http){
授权,特别授权;
http.formLogin().disable();
http.csrf().disable();
http.logout().disable();å
http.httpBasic().disable();
http.httpBasic()。
authenticationManager(this::authenticate);
authorize=http.authorizeExchange();
authorize.anyExchange().access(this::check);
返回http.build();
}
专用Mono检查(Mono身份验证、授权上下文){
返回authentication.map(a->this.checkAuthorizations(a,context)).map(grated->newauthorizationdecision(grated));
}
专用布尔检查授权(身份验证a、授权上下文){
布尔ret=false;
字符串名称=a.getName();
如果(a.isAuthenticated()){
logger.info(String.format(“找到%s,授权…”,名称));
ret=userPermissionTable.canAccess(“XXXX”,context.getExchange().getRequest().getPath().value(),context.getExchange().getRequest().getMethodValue());
logger.info(String.format(“%s已授予访问权限:%B”,名称,ret));
}
返回ret;
}
@凌驾
公共Mono身份验证(身份验证){
可完成的未来杯;
认证认证;
字符串名称=null;
auth=身份验证;
auth.setAuthenticated(false);
试一试{
name=authentication.getName();
logger.info(String.format(“正在缓存中查找%s…”,name));
userPermissionTable=(UserPermissions)redisCache.opsForValue().get(名称);
if(userPermissionTable==null){
logger.info(String.format(“不在缓存中,身份验证:%s…”,name));
cup=permissions.getPermissionsForUser(名称,身份验证.getCredentials().toString());
userPermissionTable=cup.get(1000,时间单位为毫秒);
redisCache.opsfervalue().set(名称、userPermissionTable、userPermissionTable.gettl()、TimeUnit.MINUTES);
auth=新用户名PasswordAuthenticationToken(authentication.getPrincipal(),authentication.getCredentials(),null);
logger.info(String.format(“已验证的:%s”,名称));
}
否则{
auth=新用户名PasswordAuthenticationToken(authentication.getPrincipal(),authentication.getCredentials(),null);
expire(名称,userPermissionTable.getTTL(),TimeUnit.MINUTES);
}
}捕获(例外e){
logger.info(String.format(“身份验证失败:%s”,名称));
}
返回Mono.just(auth);
}
}

伙计们,我还有一个自定义的登录页面,其中包含表单操作:-/login/authenticatespring对于安全性来说非常棘手,如果它在声明spring boot时出现在路径中,它就会启动,阻止一切,因为它没有配置。是的,这就是为什么我要覆盖index和auth方法,但由于某些原因,它无法识别我为person对象提供的用户名和密码。我的观点是,当安全jar位于路径中,并且没有进行安全更改时,在设置安全性之前,它通常会返回权限错误,这是我的spring boot体验,我们是否会通过提供用户名和密码凭据来设置安全性?