(Spring Security)实战干货二:标准登录表单,除用户名密码外增添其他字段

一、引言

上一篇,我们对自定义权鉴进行了实现,但是在很多的登录场景中,用户名和密码不是仅有的用于登录认证授权的字段,会有各类的情况。比如:学校系统登录时需要加选学生或老师身份,多组织系统登录时需要选择组织(域)等等。所以这篇文章,我们就是来用SS实现这类场景登录。


二、首先需要了解的

1、Sping Security的作用流程

在这里插入图片描述

2、默认的过滤器

别名过滤器类 Filter Class命名空间元素或属性
CHANNEL_FILTERChannelProcessingFilterhttp/intercept-url@requires-channel
CONCURRENT_SESSION_FILTERConcurrentSessionFiltersession-management/concurrency-control
SECURITY_CONTEXT_FILTERSecurityContextPersistenceFilterhttp
LOGOUT_FILTERLogoutFilterhttp/logout
X509_FILTERX509AuthenticationFilterhttp/x509
PRE_AUTH_FILTERAstractPreAuthenticatedProcessingFilter SubclassesN/A
CAS_FILTERCasAuthenticationFilterN/A
FORM_LOGIN_FILTERUsernamePasswordAuthenticationFilterhttp/form-login
BASIC_AUTH_FILTERBasicAuthenticationFilterhttp/http-basic
SERVLET_API_SUPPORT_FILTERSecurityContextHolderAwareFilterhttp/@servlet-api-provision
REMEMBER_ME_FILTERRememberMeAuthenticationFilterhttp/remember-me
ANONYMOUS_FILTERAnonymousAuthenticationFilterhttp/anonymous
SESSION_MANAGEMENT_FILTERSessionManagementFiltersession-management
EXCEPTION_TRANSLATION_FILTERExceptionTranslationFilterhttp
FILTER_SECURITY_INTERCEPTORFilterSecurityInterceptorhttp
SWITCH_USER_FILTERSwitchUserFilterN/A

验证过程按照这样顺序自上而下进行。

3、自定义过滤器位置

在spring-security.xml配置中,需要根据自定义过滤器的场景需要将过滤器放置在相关的位置上。
根据上表的顺序来指定。
before:在对应过滤器之前;
after:在对应过滤器之后;
position:在对应过滤器上。

三、代码实现

1、实现思路

用Spring Security提供的实现(DaoAuthenticationProvider和UsernamePasswordToken)。
关键组件包括:
SimpleAuthenticationFilter - UsernamePasswordAuthenticationFilter的扩展
SimpleUserDetailsService - UserDetailsService的实现
User - Spring Security提供的User类的扩展,它声明了我们的额外域字段

配置Spring Security,将SimpleAuthenticationFilter插入到过滤器链中,声明安全规则并连接依赖项
login.html - 收集用户名,密码和组织域的登录页面

2、主要代码

代码略去空间命名已经import部分。

Authentication Filter

SimpleAuthenticationFilter中,组织域和用户名字段是从请求中提取。连接这些值并使用它们来创建UsernamePasswordAuthenticationToken的实例。

public class OursPlusAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private String organizationParameter = "organization";

    protected String obtainOrganization(HttpServletRequest request) {
        return request.getParameter(this.organizationParameter);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        UsernamePasswordAuthenticationToken authRequest = getAuthRequest(request);
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    private UsernamePasswordAuthenticationToken getAuthRequest(HttpServletRequest request) {
        String username = obtainUsername(request);
        String password = obtainPassword(request);
        String organization = obtainOrganization(request);
        String usernameOrganization = String.format("%s%s%s", username.trim(), String.valueOf(Character.LINE_SEPARATOR), organization);
        return new UsernamePasswordAuthenticationToken(usernameOrganization, password);
    }
}

其中“organization”就是新增的组织域字段。

UserDetails服务

UserDetailsService的loadUserByUsername方法,改造实现提取用户名和组织域。然后将值传递给UserRepository以获取用户。

public class OursPlusUserDetailsService implements UserDetailsService {
    @Autowired
    private IUsersService usersService;
    @Autowired
    private OursUser oursUser;

    @Override
    public UserDetails loadUserByUsername(String plusUsername) throws UsernameNotFoundException {
        //整合organization后相关处理
        String[] usernameOrganization = StringUtils.split(plusUsername, String.valueOf(Character.LINE_SEPARATOR));
        if (usernameOrganization == null || usernameOrganization.length != 2) {
            throw new UsernameNotFoundException("需完善用户名及企业组织完善");
        }
        //查询用户
        Users users = usersService.findByUsername(usernameOrganization[0]);
        if (users == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        //组织id
        long organizationId = Integer.parseInt(usernameOrganization[1]);
        //一般用户权限获取
        List<String> roles = usersService.findRolesCodeByUserIdAndOrgId(users.getId(), organizationId);
        List<String> rules = usersService.findRuleNameByUserIdAndOrgId(users.getId(), organizationId);

        List<String> authorities;
        authorities = oursUser.getAuthorities(users.getId(), roles, rules);
        User user = new User(users.getUsername(), users.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(String.join(",", authorities)));
        return user;
    }
}
oursUser类

额外的自定义类,主要是封装一些出来方法处理一些数据库权鉴,本文章的代码需要基于上一篇文章《(Spring Security)实战干货一:自定义权鉴的实现》的代码,主要是理解基于数据库的认证与授权。

public class OursUser {
    @Autowired
    private IUsersService usersService;

    /**
     * 获取当前登录用户名
     *
     * @return
     */
    public String getLoginUserName() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        User principal = (User) authentication.getPrincipal();
        return principal.getUsername();
    }

    /**
     * 获取当前登录用户所有信息(除密码外)
     *
     * @return
     */
    public Users get() {
        Users users;
        String username = getLoginUserName();
        if (username.length() != 0) {
            users = usersService.findUserInfoByUsername(username);
        } else {
            users = null;
        }
        return users;
    }

    /**
     * 获取权限配置
     * @param userId
     * @param roles
     * @param rules
     * @return
     */
    public List<String> getAuthorities(Integer userId,List<String> roles,List<String> rules){
        List<String> authorities=new ArrayList<>();
        if(userId==1){
            List<String> adminRules=usersService.findAllRuleName();
            authorities.add("ROLE_ADMIN");
            authorities.addAll(adminRules);
        }else{
            roles=roles.stream()
                    .map(prc->"ROLE_"+prc)
                    .collect(Collectors.toList());
            authorities.addAll(roles);
            authorities.addAll(rules);
        }
        return authorities;
    }
}

以上存在笔者设定的权限逻辑,可以忽略,按自己的需求设定。

3、SS配置

        ...
        <security:custom-filter ref="customFilters" before="FORM_LOGIN_FILTER"/>
        ...
            <bean id="oursPlusAuthenticationFilter" class="index.OursPlusAuthenticationFilter"/>
		    <bean id="customFilters" class="org.springframework.web.filter.CompositeFilter">
		        <property name="filters">
		            <list>
		                <ref bean="oursPlusAuthenticationFilter"/>
		            </list>
		        </property>
		    </bean>
        ...

以上配置文件是截取了部分配置信息,可结合上一篇文章,了解配置信息。

4、登录页面

	<form autoComplete="off" action="/" method="post">
        <div>
            <div>
                <label>用户名</label>
                <input type="text" name="username" placeholder="用户名">
            </div>
            <div>
                <label>密码</label>
                <input type="password" name="password" placeholder="密码">
            </div>
            <div>
                <label>组织域</label>
                <input type="text" name="organization" placeholder="组织域">
            </div>
            <div>
                <input type="submit" value="登 入">
            </div>
        </div>
    </form>

以上是实现的主要思路及代码,希望对大家有帮助,同时不足之处,希望指出。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Security 默认不提供用户名密码,而是通过配置文件进行自定义设置。在Spring Security的配置文件中,可以通过使用`UserDetailsService`接口的实现类来创建用户,并配置他们的用户名密码。下面是一个示例的配置文件内容: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/public").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/dashboard") .permitAll() .and() .logout() .logoutUrl("/logout") .permitAll(); } @Bean public UserDetailsService userDetailsService() { UserDetails user = User.withUsername("username") .password(passwordEncoder().encode("password")) .roles("USER") .build(); return new InMemoryUserDetailsManager(user); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } } ``` 在上述示例中,使用`inMemoryAuthentication()`方法在内存中创建了一个用户,该用户的用户名为"username",密码为"password",并且该用户角色为"USER"。这些设置是通过调用`UserDetailsService`的实现类`InMemoryUserDetailsManager`进行配置的。 需要注意的是,在实际的应用中,强烈建议使用数据库或其他安全存储方式存储用户信息,并通过`UserDetailsService`从存储中获取用户信息,而不是直接在配置文件中硬编码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

希克

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值