การเพิ่มผู้มีอำนาจใน AuthenticationSuccessHandler

ฉันกำลังทำงานกับเว็บไซต์ Spring MVC และเพิ่มการตรวจสอบสิทธิ์กับ Active Directory บน LDAP บริษัทไม่ต้องการใช้หน่วยงาน AD เพื่อแมปสิทธิ์สำหรับเว็บไซต์ เรามีฐานข้อมูลที่แสดงรายการสิทธิ์ของผู้ใช้แต่ละราย ดังนั้นฉันจึงพยายามเชื่อมต่อกับสิ่งนั้น รับสิทธิ์ และเพิ่มลงในโทเค็นการรับรองความถูกต้องของผู้ใช้

เมื่อฉันเริ่มต้นครั้งแรก ฉันกำลังแมปอำนาจของกลุ่มผู้ใช้ AD ด้วย GrantedAuthoritiesMapper และฉันก็ใช้งานได้ ดูเหมือนว่านี้:

public class ActiveDirectoryGrantedAuthoritiesMapper implements GrantedAuthoritiesMapper {

    private static final String ROLE_ADMIN = "adminUserGroup";

    public ActiveDirectoryGrantedAuthoritiesMapper()
    { }

    public Collection<? extends GrantedAuthority> mapAuthorities(
            final Collection<? extends GrantedAuthority> authorities)
    {

        Set<CustomAuthority> roles = EnumSet.noneOf(CustomAuthority.class);

        for (GrantedAuthority authority : authorities)
        {
            if (ROLE_ADMIN.equals(authority.getAuthority()))
            {
                roles.add(CustomAuthority.ROLE_ADMIN);
            }
            //Default role for all users.
            roles.add(CustomAuthority.ROLE_EMPLOYEE);
        }
        return roles;
    }
}

ตอนนี้ฉันกำลังพยายามแปลงมันเพื่อสอบถามฐานข้อมูลของเราเพื่อขอสิทธิ์ ฉันย้ายออกจาก GrantedAuthoritiesMapper เพื่อทำเช่นนี้ด้วยเหตุผลสองประการ ก่อนอื่นฉันไม่ได้ใช้เจ้าหน้าที่จาก LDAP แล้วทำไมต้องสกัดกั้นพวกเขาด้วย? และเนื่องจากฉันไม่สามารถทราบวิธีรับชื่อผู้ใช้ที่เข้าสู่ระบบภายใน GrantedAuthoritiesMapper ฉันลองใช้ SecurityContext แต่มันให้ NullPointerException ทุกครั้งที่ฉันพยายามโทร context.getAuthentication().getName() ฉันคิดว่าเพราะผู้ใช้ยังไม่ผ่านการรับรองความถูกต้องโดยสมบูรณ์

ดังนั้นฉันจึงเปลี่ยนมาใช้ AuthenticationSuccessHandler ฉันพยายามรักษาตรรกะให้เหมือนเดิม ฉันกำลังพยายามเพิ่มบทบาทให้กับโทเค็นการตรวจสอบสิทธิ์ของผู้ใช้ด้วย authentication.getAuthorities().add(...); แต่ฉันได้รับข้อผิดพลาดที่ CustomAuthority ของฉันไม่ขยาย GrantedAuthority มันไม่ได้ขยาย แต่มันใช้อินเทอร์เฟซ ฉันสงสัยว่านี่เป็นเพราะมันเป็น enum หรือไม่ ดังนั้นฉันจึงเปลี่ยนเป็นคลาสและฉันยังคงได้รับข้อผิดพลาดอยู่ นี่คือโค้ดสำหรับ AuthenticationSuccessHandler แบบกำหนดเองที่ฉันมีตอนนี้:

public class CustomAuthoritiesMapper implements AuthenticationSuccessHandler 
{
    private CustomPermissionDAO permissionsDao = new CustomPermissionDAO();

    private static final String ROLE_ADMIN = "ADMIN_ACCOUNT";

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException
    {
        List<GrantedAuthority> roles = new ArrayList<GrantedAuthority>();

        List<DatabasePermission> permissionsForUser = permissionsDao.getPermissionByUsername(authentication.getName());

        for (DatabasePermission permission : permissionsForUser)
        {
            if (ROLE_ADMIN.equals( permission.getTag() ))
            {
                roles.add(new CustomAuthority("ROLE_ADMIN"));
            }
            //Default role for all users.
            roles.add(new DashboardAuthority("ROLE_EMPLOYEE"));
        }
        for(GrantedAuthority auth : roles)
        {
            authentication.getAuthorities().add(auth);
        }
    }
}

ฉันได้ลองทุกอย่างที่ฉันนึกได้แทบทุกชุดแล้ว ฉันได้เปลี่ยน List<GrantedAuthority> เป็นรายการวัตถุ CustomAuthority ฉันได้ลองใช้ addAll(roles) แทนที่จะเพิ่มแต่ละรายการ .. ทุกครั้งที่ฉันได้รับข้อผิดพลาดเดียวกันนี้:

เมธอด add(capture#1-of ? extensions GrantedAuthority) ในประเภท Collection ไม่สามารถใช้ได้กับอาร์กิวเมนต์ (GrantedAuthority)

และรหัส CustomAuthority:

public class CustomAuthority implements GrantedAuthority
{
    private String name;

    public CustomAuthority(String name)
    {
        this.name = name;
    }

    public String getAuthority() {
        return name;
    }
}

ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชมมาก

จากการดู "คำถามที่เกี่ยวข้อง" ดูเหมือนว่า Authentication.getName() อาจใช้งานไม่ได้ที่นี่ แต่ฉันต้องการทราบว่าเหตุใดฉันจึงไม่สามารถเพิ่มสิทธิ์ที่ฉันต้องการเพิ่มในหน่วยงานของผู้ใช้ก่อนที่จะแก้ไขปัญหานั้น


person JDiPierro    schedule 12.07.2013    source แหล่งที่มา


คำตอบ (2)


มันไม่ใช่คำตอบสำหรับคำถามเดิมของคุณ มันเป็นข้อเสนอเกี่ยวกับวิธีการทำ coud นี้ในอีกทางหนึ่ง

สิ่งที่ดูผิดปกติสำหรับฉันคือคุณใช้ AuthenticationSuccessHandler สำหรับสิ่งที่ควรจะทำโดย AuthenticationManager และ AuthenticationProviders ลองนึกภาพ AuthenticationProviders สองตัว อันหนึ่งสำหรับ LDAP และอีกอันสำหรับ DB ปัญหาคือหากคุณรวมเข้าด้วยกันโดยใช้ค่าเริ่มต้น AuthenticationManager ระบบจะใช้เฉพาะผู้ให้บริการรายแรกเท่านั้น คุณสามารถเตรียม AuthenticationManager แบบกำหนดเองโดยใช้ตรรกะที่แตกต่างกันเล็กน้อย:

  • มันรู้เกี่ยวกับผู้ให้บริการสองราย
  • มันเรียกพวกเขาตามลำดับที่ถูกต้อง
  • โดยจะรวมผลลัพธ์การรับรองความถูกต้องสองรายการจากผู้ให้บริการสองรายให้เป็นผลลัพธ์ระดับโลกรายการเดียว โดยนำข้อมูลรับรองผู้ใช้จาก LDAP และหน่วยงานจาก DB

ข้อเสีย: นักพัฒนาใหม่ที่ต้องการเพิ่มผู้ให้บริการการรับรองความถูกต้องรายที่สามอาจแปลกใจกับ AuthenticationManager ที่กำหนดเอง

ข้อดี: ฉันคิดว่าโค้ดจะเหมาะสมในตำแหน่งที่ถูกต้อง

หวังว่านี่จะช่วยได้

person Maksym Demidas    schedule 15.07.2013

เหตุผลที่คุณไม่สามารถรับชื่อผู้ใช้ปัจจุบันจาก SecurityContext ได้เนื่องจากยังไม่มีการเติมข้อมูล (คุณยังคงทำงานเพื่อหาว่าบทบาทใดที่จะรวมไว้ในนั้น)

ทางเลือกหนึ่งคือการใช้ LdapAuthenticationProvider และ LdapAuthoritiesPopulator ตามที่อธิบายไว้ใน คำถามที่พบบ่อย นี่คือตัวอย่างจากคำถามที่พบบ่อย

public class MyAuthoritiesPopulator implements LdapAuthoritiesPopulator {
    @Autowired
    JdbcTemplate template;

    public List<GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
        List<GrantedAuthority> = template.query("select role from roles where username = ?",
                                                   new String[] {username},
                                                   new RowMapper<GrantedAuthority>() {
            /**
             *  We're assuming here that you're using the standard convention of using the role
             *  prefix "ROLE_" to mark attributes which are supported by Spring Security's RoleVoter.
             */
            public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException {
                return new GrantedAuthorityImpl("ROLE_" + rs.getString(1));
            }
        });
    }
}

สิ่งนี้น่าจะได้ผลสำหรับคุณเพราะมันจะส่งชื่อผู้ใช้ให้คุณ ใช้ชื่อผู้ใช้นั้นสำหรับแบบสอบถามเพื่อรับบทบาทสำหรับผู้ใช้

ตามที่กล่าวไว้ในคำถามที่พบบ่อย คุณจะต้องเชื่อมต่อสิ่งนั้นเข้ากับ LdapAuthenticationProvider ด้วย LdapAuthoritiesPopulator ที่กำหนดเอง

อีกทางเลือกหนึ่งคือการฉีด UserDetailsContextMapper แบบกำหนดเอง ค่าเริ่มต้นคือ LdapUserDetailsMapper ซึ่งจะทำให้คุณเข้าใจถึงวิธีนำตรรกะไปใช้

person Rob Winch    schedule 12.07.2013
comment
ฉันพบวิธีแฮ็กงานใน SuccessHandler แล้ว แต่นี่ดูเหมือนจะเป็นวิธีที่เหมาะสมที่จะทำ ขอบคุณที่ชี้ให้ฉันไปทางนั้น - person JDiPierro; 15.07.2013
comment
ที่จริงแล้ว .. ดูเหมือนว่าเป็นไปไม่ได้ที่จะใช้ LdapAuthoritiesPopulator กับ ActiveDirectoryLdapAuthenticationProvider ใช่ไหม - person JDiPierro; 15.07.2013
comment
คุณถูกต้อง (คำตอบของฉันระบุว่าใช้ LdapAuthenticationProvider หากคุณต้องการใช้ LdapAuthoritiesPopulator) หรืออีกทางหนึ่ง หากคุณต้องการใช้ ActiveDirectoryLdapAuthenticationProvider ให้ใช้ UserDetailsContextMapper - person Rob Winch; 15.07.2013